diff --git a/CMakeLists.txt b/CMakeLists.txt index f213942057..5a83cf0fc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,6 +256,7 @@ if(LIBRETRO) if(NOT MSVC) add_compile_options(-fPIC) endif() + add_compile_definitions(HAVE_LIBRETRO_VFS) endif() if(ANDROID) @@ -944,6 +945,10 @@ add_library(Common STATIC Common/TimeUtil.h ) +if(LIBRETRO) + target_include_directories(Common PRIVATE libretro/libretro-common/include) +endif() + include_directories(Common) setup_target_project(Common Common) @@ -1671,7 +1676,7 @@ if(ANDROID) endif() endif() -if (IOS) +if (IOS AND NOT LIBRETRO) set(nativeExtra ${nativeExtra} ${NativeAppSource}) endif() @@ -1685,6 +1690,10 @@ add_library(native STATIC ext/jpge/jpge.h ) +if(LIBRETRO) + target_include_directories(native PRIVATE libretro/libretro-common/include) +endif() + if(LINUX AND NOT ANDROID) set(RT_LIB rt) endif() @@ -2530,6 +2539,10 @@ add_library(${CoreLibName} ${CoreLinkType} ${CMAKE_CURRENT_BINARY_DIR}/git-version.cpp ) +if(LIBRETRO) + target_include_directories(${CoreLibName} PRIVATE libretro/libretro-common/include) +endif() + if(ANDROID) set(CoreExtraLibs ${CoreExtraLibs} android) if(X86_64) diff --git a/Common/Data/Format/IniFile.cpp b/Common/Data/Format/IniFile.cpp index c2c5e3f33f..98a9af4216 100644 --- a/Common/Data/Format/IniFile.cpp +++ b/Common/Data/Format/IniFile.cpp @@ -14,7 +14,6 @@ #endif #include -#include #include #include #include diff --git a/Common/Data/Format/PNGLoad.cpp b/Common/Data/Format/PNGLoad.cpp index 51ebd5d9ac..fb9136d514 100644 --- a/Common/Data/Format/PNGLoad.cpp +++ b/Common/Data/Format/PNGLoad.cpp @@ -134,12 +134,7 @@ bool PNGHeaderPeek::IsValidPNGHeader() const { } bool pngSave(const Path &filename, const void *buffer, int w, int h, int bytesPerPixel) { - png_image png{}; - png.version = PNG_IMAGE_VERSION; - png.format = bytesPerPixel == 3 ? PNG_FORMAT_RGB : PNG_FORMAT_RGBA; - png.width = w; - png.height = h; - const int row_stride = w * bytesPerPixel; + png_bytepp row_ptrs = nullptr; FILE *fp = File::OpenCFile(filename, "wb"); if (!fp) { @@ -147,16 +142,28 @@ bool pngSave(const Path &filename, const void *buffer, int w, int h, int bytesPe return false; } - int result = png_image_write_to_stdio(&png, fp, 0, buffer, row_stride, nullptr); - - if (png.warning_or_error >= 2) { - ERROR_LOG(Log::IO, "Saving image to PNG produced errors."); + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, pngErrorHandler, pngWarningHandler); + if (png_ptr == nullptr) { + fclose(fp); + ERROR_LOG(Log::IO, "PNG encode failed."); + return false; } - png_image_free(&png); - fclose(fp); + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == nullptr) { + png_destroy_write_struct(&png_ptr, nullptr); + fclose(fp); + ERROR_LOG(Log::IO, "PNG encode failed."); + return false; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + if (row_ptrs != nullptr) { + png_free(png_ptr, row_ptrs); + } + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); - if (!result) { // Should we even do this? File::Delete(filename); @@ -164,5 +171,26 @@ bool pngSave(const Path &filename, const void *buffer, int w, int h, int bytesPe return false; } + png_set_write_fn(png_ptr, fp, [](png_structp png_ptr, png_bytep data, png_size_t size) { + if (fwrite(data, 1, size, (FILE *)png_get_io_ptr(png_ptr)) < size) { + png_error(png_ptr, "Failed to write to file."); + } + }, [](png_structp png_ptr) { + fflush((FILE *)png_get_io_ptr(png_ptr)); + }); + + png_set_IHDR(png_ptr, info_ptr, w, h, 8, bytesPerPixel == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + row_ptrs = (png_bytepp)png_malloc(png_ptr, (png_alloc_size_t)h * (png_alloc_size_t)sizeof(png_bytep)); + for (png_alloc_size_t i = 0; i < h; ++i) { + row_ptrs[i] = (png_bytep)buffer + (png_alloc_size_t)w * (png_alloc_size_t)bytesPerPixel * i; + } + png_set_rows(png_ptr, info_ptr, row_ptrs); + + png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr); + + png_free(png_ptr, row_ptrs); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); return true; } diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 292d0da7d2..be71be0fc5 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -77,6 +77,10 @@ #endif // !PPSSPP_PLATFORM(IOS) #endif // __APPLE__ +#ifdef HAVE_LIBRETRO_VFS +#include +#endif + #include "Common/Data/Encoding/Utf8.h" #include @@ -105,11 +109,33 @@ constexpr bool LOG_IO = false; #define DIR_SEP_CHRS "/" #endif +#ifdef HAVE_LIBRETRO_VFS +static retro_vfs_mkdir_t LibretroMkdirCallback = nullptr; + +// Creates a directory at the given path. Parent directories are not created if +// they are missing. If the libretro VFS is supported by the libretro frontend, +// it will be used; if the libretro VFS is not supported by the frontend, the +// mkdir function will be used instead. Returns 0 if the directory did not exist +// and was successfully created, -2 if the directory already exists or -1 if +// some other error occurred. +static int LibretroMkdir(const char *path) noexcept { + return LibretroMkdirCallback != nullptr ? LibretroMkdirCallback(path) : retro_vfs_mkdir_impl(path); +} +#endif + // This namespace has various generic functions related to files and paths. // The code still needs a ton of cleanup. // REMEMBER: strdup considered harmful! namespace File { +#ifdef HAVE_LIBRETRO_VFS +void InitLibretroVFS(const struct retro_vfs_interface_info *vfs) noexcept { + filestream_vfs_init(vfs); + path_vfs_init(vfs); + LibretroMkdirCallback = vfs->required_interface_version >= 3 ? vfs->iface->mkdir : nullptr; +} +#endif + FILE *OpenCFile(const Path &path, const char *mode) { if (LOG_IO) { INFO_LOG(Log::IO, "OpenCFile %s, %s", path.c_str(), mode); @@ -120,6 +146,7 @@ FILE *OpenCFile(const Path &path, const char *mode) { switch (path.Type()) { case PathType::NATIVE: break; +#ifndef HAVE_LIBRETRO_VFS case PathType::CONTENT_URI: // We're gonna need some error codes.. if (!strcmp(mode, "r") || !strcmp(mode, "rb") || !strcmp(mode, "rt")) { @@ -167,7 +194,7 @@ FILE *OpenCFile(const Path &path, const char *mode) { FILE *f = fdopen(descriptor, fmode); if (f && (!strcmp(mode, "at") || !strcmp(mode, "a"))) { // Append mode - not sure we got a "true" append mode, so seek to the end. - fseek(f, 0, SEEK_END); + Fseek(f, 0, SEEK_END); } return f; } else { @@ -175,12 +202,29 @@ FILE *OpenCFile(const Path &path, const char *mode) { return nullptr; } break; +#endif default: ERROR_LOG(Log::IO, "OpenCFile(%s): PathType not yet supported", path.c_str()); return nullptr; } -#if defined(_WIN32) && defined(UNICODE) +#ifdef HAVE_LIBRETRO_VFS + if (!strcmp(mode, "r") || !strcmp(mode, "rb") || !strcmp(mode, "rt")) { + INFO_LOG(Log::IO, "OpenCFile(%s): Opening content file for read.", path.c_str()); + return filestream_open(path.c_str(), RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + } else if (!strcmp(mode, "w") || !strcmp(mode, "wb") || !strcmp(mode, "wt") || !strcmp(mode, "at") || !strcmp(mode, "a")) { + INFO_LOG(Log::IO, "OpenCFile(%s): Opening content file for write.", path.c_str()); + bool append = !strcmp(mode, "at") || !strcmp(mode, "a"); + FILE *f = filestream_open(path.c_str(), append && Exists(path) ? RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING : RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE); + if (f != nullptr && append) { + Fseek(f, 0, SEEK_END); + } + return f; + } else { + ERROR_LOG(Log::IO, "OpenCFile(%s): Mode not yet supported: %s", path.c_str(), mode); + return nullptr; + } +#elif defined(_WIN32) && defined(UNICODE) #if PPSSPP_PLATFORM(UWP) && !defined(__LIBRETRO__) // We shouldn't use _wfopen here, // this function is not allowed to read outside Local and Installation folders @@ -438,7 +482,9 @@ bool Exists(const Path &path) { return Android_FileExists(path.c_str()); } -#if defined(_WIN32) +#ifdef HAVE_LIBRETRO_VFS + return path_is_valid(path.c_str()); +#elif defined(_WIN32) // Make sure Windows will no longer handle critical errors, which means no annoying "No disk" dialog #if !PPSSPP_PLATFORM(UWP) @@ -488,7 +534,9 @@ bool IsDirectory(const Path &path) { return false; } -#if defined(_WIN32) +#ifdef HAVE_LIBRETRO_VFS + return path_is_directory(path.c_str()); +#elif defined(_WIN32) WIN32_FILE_ATTRIBUTE_DATA data{}; #if PPSSPP_PLATFORM(UWP) if (!GetFileAttributesExFromAppW(path.ToWString().c_str(), GetFileExInfoStandard, &data) || data.dwFileAttributes == INVALID_FILE_ATTRIBUTES) { @@ -545,7 +593,12 @@ bool Delete(const Path &filename, bool quiet) { return false; } -#ifdef _WIN32 +#ifdef HAVE_LIBRETRO_VFS + if (filestream_delete(filename.c_str()) != 0) { + WARN_LOG(Log::IO, "Delete: DeleteFile failed on %s", filename.c_str()); + return false; + } +#elif defined(_WIN32) #if PPSSPP_PLATFORM(UWP) if (!DeleteFileFromAppW(filename.ToWString().c_str())) { WARN_LOG(Log::IO, "Delete: DeleteFile failed on %s: %s", filename.c_str(), GetLastErrorMsg().c_str()); @@ -605,7 +658,17 @@ bool CreateDir(const Path &path) { } DEBUG_LOG(Log::IO, "CreateDir('%s')", path.c_str()); -#ifdef _WIN32 +#ifdef HAVE_LIBRETRO_VFS + switch (LibretroMkdir(path.ToString().c_str())) { + case -2: + DEBUG_LOG(Log::IO, "CreateDir: mkdir failed on %s: already exists", path.c_str()); + case 0: + return true; + default: + ERROR_LOG(Log::IO, "CreateDir: mkdir failed on %s", path.c_str()); + return false; + } +#elif defined(_WIN32) #if PPSSPP_PLATFORM(UWP) if (CreateDirectoryFromAppW(path.ToWString().c_str(), NULL)) return true; @@ -717,6 +780,13 @@ bool Rename(const Path &srcFilename, const Path &destFilename) { INFO_LOG(Log::IO, "Rename: %s --> %s", srcFilename.c_str(), destFilename.c_str()); +#ifdef HAVE_LIBRETRO_VFS + if (filestream_rename(srcFilename.c_str(), destFilename.c_str()) == 0) + return true; + ERROR_LOG(Log::IO, "Rename: failed %s --> %s", + srcFilename.c_str(), destFilename.c_str()); + return false; +#else #if defined(_WIN32) && defined(UNICODE) #if PPSSPP_PLATFORM(UWP) if (MoveFileFromAppW(srcFilename.ToWString().c_str(), destFilename.ToWString().c_str())) @@ -735,6 +805,7 @@ bool Rename(const Path &srcFilename, const Path &destFilename) { ERROR_LOG(Log::IO, "Rename: failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg().c_str()); return false; +#endif } // copies file srcFilename to destFilename, returns true on success @@ -764,7 +835,7 @@ bool Copy(const Path &srcFilename, const Path &destFilename) { } INFO_LOG(Log::IO, "Copy by OpenCFile: %s --> %s", srcFilename.c_str(), destFilename.c_str()); -#ifdef _WIN32 +#if defined(_WIN32) && !defined(HAVE_LIBRETRO_VFS) #if PPSSPP_PLATFORM(UWP) if (CopyFileFromAppW(srcFilename.ToWString().c_str(), destFilename.ToWString().c_str(), FALSE)) return true; @@ -908,7 +979,9 @@ uint64_t GetFileSize(const Path &filename) { return false; } -#if defined(_WIN32) && defined(UNICODE) +#ifdef HAVE_LIBRETRO_VFS + return path_get_size(filename.c_str()); +#elif defined(_WIN32) && defined(UNICODE) WIN32_FILE_ATTRIBUTE_DATA attr; #if PPSSPP_PLATFORM(UWP) if (!GetFileAttributesExFromAppW(filename.ToWString().c_str(), GetFileExInfoStandard, &attr)) @@ -941,45 +1014,19 @@ uint64_t GetFileSize(const Path &filename) { } uint64_t GetFileSize(FILE *f) { - // This will only support 64-bit when large file support is available. - // That won't be the case on some versions of Android, at least. -#if defined(__ANDROID__) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64) - int fd = fileno(f); - - off64_t pos = lseek64(fd, 0, SEEK_CUR); - off64_t size = lseek64(fd, 0, SEEK_END); - if (size != pos && lseek64(fd, pos, SEEK_SET) != pos) { + uint64_t pos = Ftell(f); + if (Fseek(f, 0, SEEK_END) != 0) { + return 0; + } + uint64_t size = Ftell(f); + // Reset the seek position to where it was when we started. + if (size != pos && Fseek(f, pos, SEEK_SET) != 0) { // Should error here. return 0; } if (size == -1) return 0; return size; -#else -#ifdef _WIN32 - uint64_t pos = _ftelli64(f); -#else - uint64_t pos = ftello(f); -#endif - if (fseek(f, 0, SEEK_END) != 0) { - return 0; - } -#ifdef _WIN32 - uint64_t size = _ftelli64(f); - // Reset the seek position to where it was when we started. - if (size != pos && _fseeki64(f, pos, SEEK_SET) != 0) { -#else - uint64_t size = ftello(f); - // Reset the seek position to where it was when we started. - if (size != pos && fseeko(f, pos, SEEK_SET) != 0) { -#endif - // Should error here. - return 0; - } - if (size == -1) - return 0; - return size; -#endif } // creates an empty file filename, returns true on success @@ -1019,6 +1066,12 @@ bool DeleteDir(const Path &path) { return false; } +#ifdef HAVE_LIBRETRO_VFS + if (filestream_delete(path.c_str()) == 0) + return true; + ERROR_LOG(Log::IO, "DeleteDir: %s", path.c_str()); + return false; +#else #ifdef _WIN32 #if PPSSPP_PLATFORM(UWP) if (RemoveDirectoryFromAppW(path.ToWString().c_str())) @@ -1034,6 +1087,7 @@ bool DeleteDir(const Path &path) { ERROR_LOG(Log::IO, "DeleteDir: %s: %s", path.c_str(), GetLastErrorMsg().c_str()); return false; +#endif } // Deletes the given directory and anything under it. Returns true on success. @@ -1166,6 +1220,64 @@ const Path &GetExeDirectory() { return ExePath; } +int Fseek(FILE *file, int64_t offset, int whence) { +#ifdef HAVE_LIBRETRO_VFS + switch (whence) { + default: + whence = RETRO_VFS_SEEK_POSITION_START; + break; + case SEEK_CUR: + whence = RETRO_VFS_SEEK_POSITION_CURRENT; + break; + case SEEK_END: + whence = RETRO_VFS_SEEK_POSITION_END; + break; + } + return filestream_seek(file, offset, whence) != 0 ? -1 : 0; +#elif defined(_WIN32) + return _fseeki64(file, offset, whence); +#elif (defined(__ANDROID__) && __ANDROID_API__ < 24) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64) + return fseek(file, offset, whence); +#else + return fseeko(file, offset, whence); +#endif +} + +int64_t Fseektell(FILE *file, int64_t offset, int whence) { +#ifdef HAVE_LIBRETRO_VFS + switch (whence) { + default: + whence = RETRO_VFS_SEEK_POSITION_START; + break; + case SEEK_CUR: + whence = RETRO_VFS_SEEK_POSITION_CURRENT; + break; + case SEEK_END: + whence = RETRO_VFS_SEEK_POSITION_END; + break; + } + return filestream_seek(file, offset, whence) != 0 ? -1 : filestream_tell(file); +#elif defined(_WIN32) + return _fseeki64(file, offset, whence) != 0 ? -1 : _ftelli64(file); +#elif (defined(__ANDROID__) && __ANDROID_API__ < 24) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64) + return fseek(file, offset, whence) != 0 ? -1 : ftell(file); +#else + return fseeko(file, offset, whence) != 0 ? -1 : ftello(file); +#endif +} + +int64_t Ftell(FILE *file) { +#ifdef HAVE_LIBRETRO_VFS + return filestream_tell(file); +#elif defined(_WIN32) + return _ftelli64(file); +#elif defined(__ANDROID__) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64) + return ftell(file); +#else + return ftello(file); +#endif +} + IOFile::IOFile(const Path &filename, const char openmode[]) { Open(filename, openmode); @@ -1185,21 +1297,21 @@ bool IOFile::Open(const Path& filename, const char openmode[]) bool IOFile::Close() { - if (!IsOpen() || 0 != std::fclose(m_file)) + if (!IsOpen() || 0 != fclose(m_file)) m_good = false; m_file = NULL; return m_good; } -std::FILE* IOFile::ReleaseHandle() +FILE* IOFile::ReleaseHandle() { - std::FILE* const ret = m_file; + FILE* const ret = m_file; m_file = NULL; return ret; } -void IOFile::SetHandle(std::FILE* file) +void IOFile::SetHandle(FILE* file) { Close(); Clear(); @@ -1216,7 +1328,7 @@ uint64_t IOFile::GetSize() bool IOFile::Seek(int64_t off, int origin) { - if (!IsOpen() || 0 != fseeko(m_file, off, origin)) + if (!IsOpen() || 0 != Fseek(m_file, off, origin)) m_good = false; return m_good; @@ -1225,14 +1337,14 @@ bool IOFile::Seek(int64_t off, int origin) uint64_t IOFile::Tell() { if (IsOpen()) - return ftello(m_file); + return Ftell(m_file); else return -1; } bool IOFile::Flush() { - if (!IsOpen() || 0 != std::fflush(m_file)) + if (!IsOpen() || 0 != fflush(m_file)) m_good = false; return m_good; @@ -1241,7 +1353,9 @@ bool IOFile::Flush() bool IOFile::Resize(uint64_t size) { if (!IsOpen() || 0 != -#ifdef _WIN32 +#ifdef HAVE_LIBRETRO_VFS + filestream_truncate(m_file, size) +#elif defined(_WIN32) // ector: _chsize sucks, not 64-bit safe // F|RES: changed to _chsize_s. i think it is 64-bit safe _chsize_s(_fileno(m_file), size) @@ -1282,7 +1396,7 @@ bool ReadFileToStringOptions(bool textFile, bool allowShort, const Path &filenam if (textFile) { // totalRead doesn't take \r into account since they might be skipped in this mode. // So let's just ask how far the cursor got. - totalRead = ftell(f); + totalRead = Ftell(f); } success = allowShort ? (totalRead <= len) : (totalRead == len); } @@ -1296,14 +1410,13 @@ uint8_t *ReadLocalFile(const Path &filename, size_t *size) { *size = 0; return nullptr; } - fseek(file, 0, SEEK_END); - size_t f_size = ftell(file); - if ((long)f_size < 0) { + int64_t f_size = Fseektell(file, 0, SEEK_END); + if (f_size < 0) { *size = 0; fclose(file); return nullptr; } - fseek(file, 0, SEEK_SET); + Fseek(file, 0, SEEK_SET); // NOTE: If you find ~10 memory leaks from here, with very varying sizes, it might be the VFPU LUTs. uint8_t *contents = new uint8_t[f_size + 1]; if (fread(contents, 1, f_size, file) != f_size) { @@ -1351,6 +1464,7 @@ void ChangeMTime(const Path &path, time_t mtime) { return; } +#ifndef HAVE_LIBRETRO_VFS #ifdef _WIN32 _utimbuf buf{}; buf.actime = mtime; @@ -1362,6 +1476,7 @@ void ChangeMTime(const Path &path, time_t mtime) { buf.modtime = mtime; utime(path.c_str(), &buf); #endif +#endif } bool IsProbablyInDownloadsFolder(const Path &filename) { diff --git a/Common/File/FileUtil.h b/Common/File/FileUtil.h index aabbb0e34f..7d078d8167 100644 --- a/Common/File/FileUtil.h +++ b/Common/File/FileUtil.h @@ -17,7 +17,6 @@ #pragma once -#include #include #include #include @@ -137,6 +136,19 @@ const Path &GetExeDirectory(); const Path GetCurDirectory(); +// Portable version of fseek() that works with 64-bit offsets if supported by +// the operating system. +int Fseek(FILE *file, int64_t offset, int whence); + +// Portable version of fseek() that works with 64-bit offsets if supported by +// the operating system, and returns the new offset from the start of the file +// or -1 if it failed. +int64_t Fseektell(FILE *file, int64_t offset, int whence); + +// Portable version of ftell() that works with 64-bit offsets if supported by +// the operating system. +int64_t Ftell(FILE *file); + // simple wrapper for cstdlib file functions to // hopefully will make error checking easier // and make forgetting an fclose() harder @@ -156,7 +168,7 @@ public: template bool ReadArray(T* data, size_t length) { - if (!IsOpen() || length != std::fread(data, sizeof(T), length, m_file)) + if (!IsOpen() || length != fread(data, sizeof(T), length, m_file)) m_good = false; return m_good; @@ -165,7 +177,7 @@ public: template bool WriteArray(const T* data, size_t length) { - if (!IsOpen() || length != std::fwrite(data, sizeof(T), length, m_file)) + if (!IsOpen() || length != fwrite(data, sizeof(T), length, m_file)) m_good = false; return m_good; @@ -187,11 +199,11 @@ public: bool IsGood() const { return m_good; } operator bool() const { return IsGood() && IsOpen(); } - std::FILE* ReleaseHandle(); + FILE* ReleaseHandle(); - std::FILE* GetHandle() { return m_file; } + FILE* GetHandle() { return m_file; } - void SetHandle(std::FILE* file); + void SetHandle(FILE* file); bool Seek(int64_t off, int origin); uint64_t Tell(); @@ -202,15 +214,23 @@ public: // clear error state void Clear() { m_good = true; +#ifndef HAVE_LIBRETRO_VFS #undef clearerr std::clearerr(m_file); +#endif } private: - std::FILE *m_file = nullptr; + FILE *m_file = nullptr; bool m_good = true; }; +#ifdef HAVE_LIBRETRO_VFS +// Call this on libretro core initialization with the libretro virtual file +// system interface. +void InitLibretroVFS(const struct retro_vfs_interface_info *vfs) noexcept; +#endif + // TODO: Refactor, this was moved from the old file_util.cpp. // Whole-file reading/writing diff --git a/Common/File/Path.h b/Common/File/Path.h index 5d437a0bcb..2b3cf3c535 100644 --- a/Common/File/Path.h +++ b/Common/File/Path.h @@ -5,6 +5,10 @@ #include #include +#ifdef HAVE_LIBRETRO_VFS +#include "libretro/LibretroFileStreamTransforms.h" +#endif + #if defined(__APPLE__) #if TARGET_OS_IPHONE diff --git a/Common/File/VFS/DirectoryReader.cpp b/Common/File/VFS/DirectoryReader.cpp index 6e13eb6ad9..60e2940390 100644 --- a/Common/File/VFS/DirectoryReader.cpp +++ b/Common/File/VFS/DirectoryReader.cpp @@ -77,9 +77,9 @@ VFSOpenFile *DirectoryReader::OpenFileForRead(VFSFileReference *vfsReference, si if (!file) { return nullptr; } - fseek(file, 0, SEEK_END); - *size = ftell(file); - fseek(file, 0, SEEK_SET); + File::Fseek(file, 0, SEEK_END); + *size = File::Ftell(file); + File::Fseek(file, 0, SEEK_SET); DirectoryReaderOpenFile *openFile = new DirectoryReaderOpenFile(); openFile->file = file; return openFile; @@ -87,7 +87,7 @@ VFSOpenFile *DirectoryReader::OpenFileForRead(VFSFileReference *vfsReference, si void DirectoryReader::Rewind(VFSOpenFile *vfsOpenFile) { DirectoryReaderOpenFile *openFile = (DirectoryReaderOpenFile *)vfsOpenFile; - fseek(openFile->file, 0, SEEK_SET); + File::Fseek(openFile->file, 0, SEEK_SET); } size_t DirectoryReader::Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) { diff --git a/Common/File/VFS/ZipFileReader.cpp b/Common/File/VFS/ZipFileReader.cpp index 7ff871ecbc..d6b2751b6a 100644 --- a/Common/File/VFS/ZipFileReader.cpp +++ b/Common/File/VFS/ZipFileReader.cpp @@ -15,41 +15,149 @@ #include "Common/File/VFS/ZipFileReader.h" #include "Common/StringUtils.h" -ZipFileReader *ZipFileReader::Create(const Path &zipFile, const char *inZipPath, bool logErrors) { - int error = 0; - zip *zip_file; - if (zipFile.Type() == PathType::CONTENT_URI) { - int fd = File::OpenFD(zipFile, File::OPEN_READ); - if (!fd) { - if (logErrors) { - ERROR_LOG(Log::IO, "Failed to open FD for '%s' as zip file", zipFile.c_str()); +ZipContainer::ZipContainer() noexcept : sourceData_(nullptr), zip_(nullptr) {} + +ZipContainer::ZipContainer(const Path &path) : sourceData_(new SourceData {path, nullptr}), zip_(nullptr) { + zip_source_t *source = zip_source_function_create(SourceCallback, sourceData_, nullptr); + if (source != nullptr && (zip_ = zip_open_from_source(source, ZIP_RDONLY, nullptr)) == nullptr) { + zip_source_free(source); + } +} + +ZipContainer::ZipContainer(ZipContainer &&other) noexcept { + *this = std::move(other); +} + +ZipContainer &ZipContainer::operator=(ZipContainer &&other) noexcept { + sourceData_ = other.sourceData_; + zip_ = other.zip_; + other.sourceData_ = nullptr; + other.zip_ = nullptr; + return *this; +} + +ZipContainer::~ZipContainer() { + close(); +} + +void ZipContainer::close() noexcept { + if (zip_ != nullptr) { + zip_close(zip_); + zip_ = nullptr; + } + if (sourceData_ != nullptr) { + delete sourceData_; + sourceData_ = nullptr; + } +} + +ZipContainer::operator zip_t *() const noexcept { + return zip_; +} + +zip_int64_t ZipContainer::SourceCallback(void *userdata, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + SourceData &sourceData = *(SourceData *)userdata; + + switch (cmd) { + case ZIP_SOURCE_SUPPORTS: + return zip_source_make_command_bitmap( + ZIP_SOURCE_SUPPORTS, + ZIP_SOURCE_OPEN, + ZIP_SOURCE_READ, + ZIP_SOURCE_CLOSE, + ZIP_SOURCE_STAT, + ZIP_SOURCE_ERROR, + ZIP_SOURCE_SEEK, + ZIP_SOURCE_TELL, + -1); + + case ZIP_SOURCE_OPEN: + { + FILE *newFile = File::OpenCFile(sourceData.path, "rb"); + if (newFile == nullptr) { + return -1; + } + sourceData.file = newFile; + return 0; } - return nullptr; - } - zip_file = zip_fdopen(fd, 0, &error); - } else { - zip_file = zip_open(zipFile.c_str(), 0, &error); - } - if (!zip_file) { - if (logErrors) { - ERROR_LOG(Log::IO, "Failed to open %s as a zip file", zipFile.c_str()); - } - return nullptr; - } + case ZIP_SOURCE_READ: + { + size_t n = fread(data, 1, len, sourceData.file); + return ferror(sourceData.file) ? -1 : n; + } + case ZIP_SOURCE_CLOSE: + fclose(sourceData.file); + sourceData.file = nullptr; + return 0; + + case ZIP_SOURCE_STAT: + if (sourceData.file == nullptr) { + zip_stat_t *stat = (zip_stat_t *)data; + zip_stat_init(stat); + stat->valid = 0; + } else { + int64_t pos = File::Ftell(sourceData.file); + if (pos == -1) { + return -1; + } + if (File::Fseek(sourceData.file, 0, SEEK_END) != 0) { + return -1; + } + int64_t size = File::Ftell(sourceData.file); + if (size != pos && File::Fseek(sourceData.file, pos, SEEK_SET) != 0) { + return -1; + } + zip_stat_t *stat = (zip_stat_t *)data; + zip_stat_init(stat); + stat->valid = ZIP_STAT_SIZE; + stat->size = size; + } + return 0; + + case ZIP_SOURCE_ERROR: + return ZIP_ER_INTERNAL; + + case ZIP_SOURCE_SEEK: + { + zip_source_args_seek_t *args = (zip_source_args_seek_t *)data; + if (args == nullptr) { + return -1; + } + return File::Fseek(sourceData.file, args->offset, args->whence) ? -1 : 0; + } + + case ZIP_SOURCE_TELL: + return File::Ftell(sourceData.file); + + default: + return -1; + } +} + +ZipFileReader *ZipFileReader::Create(const Path &zipFile, const char *inZipPath, bool logErrors) { // The inZipPath is supposed to be a folder, and internally in this class, we suffix // folder paths with '/', matching how the zip library works. std::string path = inZipPath; if (!path.empty() && path.back() != '/') { path.push_back('/'); } - return new ZipFileReader(zip_file, zipFile, path); + + ZipContainer zip(zipFile); + if (zip == nullptr) { + if (logErrors) { + ERROR_LOG(Log::IO, "Failed to open %s as a zip file", zipFile.c_str()); + } + return nullptr; + } + + return new ZipFileReader(std::move(zip), zipFile, path); } ZipFileReader::~ZipFileReader() { std::lock_guard guard(lock_); - zip_close(zip_file_); + zip_file_.close(); } uint8_t *ZipFileReader::ReadFile(const char *path, size_t *size) { @@ -320,30 +428,17 @@ void ZipFileReader::CloseFile(VFSOpenFile *vfsOpenFile) { } bool ReadSingleFileFromZip(Path zipFile, const char *path, std::string *data, std::mutex *mutex) { - zip *zip = nullptr; - int error = 0; - if (zipFile.Type() == PathType::CONTENT_URI) { - int fd = File::OpenFD(zipFile, File::OPEN_READ); - if (!fd) { - return false; - } - zip = zip_fdopen(fd, 0, &error); - } else { - zip = zip_open(zipFile.c_str(), 0, &error); - } - - if (!zip) { + ZipContainer zip(zipFile); + if (zip == nullptr) { return false; } struct zip_stat zstat; if (zip_stat(zip, path, ZIP_FL_NOCASE | ZIP_FL_UNCHANGED, &zstat) != 0) { - zip_close(zip); return false; } zip_file *file = zip_fopen_index(zip, zstat.index, ZIP_FL_UNCHANGED); if (!file) { - zip_close(zip); return false; } if (mutex) { @@ -356,13 +451,11 @@ bool ReadSingleFileFromZip(Path zipFile, const char *path, std::string *data, st } data->resize(0); zip_fclose(file); - zip_close(zip); return false; } if (mutex) { mutex->unlock(); } zip_fclose(file); - zip_close(zip); return true; } diff --git a/Common/File/VFS/ZipFileReader.h b/Common/File/VFS/ZipFileReader.h index 4c40877f17..b2d7d3121c 100644 --- a/Common/File/VFS/ZipFileReader.h +++ b/Common/File/VFS/ZipFileReader.h @@ -9,11 +9,34 @@ #include #include #include +#include #include "Common/File/VFS/VFS.h" #include "Common/File/FileUtil.h" #include "Common/File/Path.h" +class ZipContainer { +public: + ZipContainer() noexcept; + ZipContainer(const Path &path); + ~ZipContainer(); + ZipContainer(const ZipContainer &) = delete; + ZipContainer(ZipContainer &&) noexcept; + ZipContainer &operator=(const ZipContainer &) = delete; + ZipContainer &operator=(ZipContainer &&) noexcept; + void close() noexcept; + operator zip_t *() const noexcept; + +private: + struct SourceData { + Path path; + FILE *file; + } *sourceData_; + zip_t *zip_; + + static zip_int64_t SourceCallback(void *userdata, void *data, zip_uint64_t len, zip_source_cmd_t cmd); +}; + class ZipFileReader : public VFSBackend { public: static ZipFileReader *Create(const Path &zipFile, const char *inZipPath, bool logErrors = true); @@ -45,11 +68,11 @@ public: } private: - ZipFileReader(zip *zip_file, const Path &zipPath, const std::string &inZipPath) : zip_file_(zip_file), zipPath_(zipPath), inZipPath_(inZipPath) {} + ZipFileReader(ZipContainer &&zip, const Path &zipPath, const std::string &inZipPath) : zip_file_(std::move(zip)), zipPath_(zipPath), inZipPath_(inZipPath) {} // Path has to be either an empty string, or a string ending with a /. bool GetZipListings(const std::string &path, std::set &files, std::set &directories); - zip *zip_file_ = nullptr; + ZipContainer zip_file_; std::mutex lock_; std::string inZipPath_; Path zipPath_; diff --git a/Common/Log/LogManager.cpp b/Common/Log/LogManager.cpp index 103f4e7d78..dd9836100f 100644 --- a/Common/Log/LogManager.cpp +++ b/Common/Log/LogManager.cpp @@ -382,6 +382,10 @@ void OutputDebugStringUTF8(const char *p) { #endif +#ifdef HAVE_LIBRETRO_VFS +#undef fprintf +#endif + void LogManager::StdioLog(const LogMessage &message) { #if PPSSPP_PLATFORM(ANDROID) #ifndef LOG_APP_NAME diff --git a/Common/Net/Resolve.cpp b/Common/Net/Resolve.cpp index 23d3bf989d..644de89310 100644 --- a/Common/Net/Resolve.cpp +++ b/Common/Net/Resolve.cpp @@ -54,6 +54,139 @@ void Shutdown() #endif } +bool HostPortExists(const std::string& host, int port, int timeout_ms) { + if (host.empty() || (port <= 0 || port > 65535) || timeout_ms < 0) return false; + + addrinfo hints; + addrinfo* res = nullptr; + + std::memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; // TCP + hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 + + int gai = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); + if (gai != 0) { + // getaddrinfo failed (DNS resolve failed or bad port) + return false; + } + + bool ok = false; + + for (addrinfo* p = res; p != nullptr && !ok; p = p->ai_next) { + // create socket + int sockfd = +#ifdef _WIN32 + (int)socket(p->ai_family, p->ai_socktype, p->ai_protocol); +#else + socket(p->ai_family, p->ai_socktype, p->ai_protocol); +#endif + if (sockfd < 0) { + continue; + } + + // make non-blocking +#ifdef _WIN32 + unsigned long mode = 1; + ioctlsocket((SOCKET)sockfd, FIONBIO, &mode); +#else + int flags = fcntl(sockfd, F_GETFL, 0); + if (flags == -1) flags = 0; + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); +#endif + + // try connect + int conn = connect(sockfd, p->ai_addr, (int)p->ai_addrlen); +#ifdef _WIN32 + if (conn == 0) { + ok = true; // immediate success + } + else { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK || err == WSAEINPROGRESS) { + // fall through to select + } + else { + // immediate failure + } + } +#else + if (conn == 0) { + ok = true; // immediate success + } + else { + if (errno == EINPROGRESS) { + // fall through to select + } + else { + // immediate failure + } + } +#endif + + if (!ok) { + // wait for writable with timeout + fd_set writefds; + FD_ZERO(&writefds); +#ifdef _WIN32 + FD_SET((SOCKET)sockfd, &writefds); +#else + FD_SET(sockfd, &writefds); +#endif + + fd_set exceptfds; + FD_ZERO(&exceptfds); +#ifdef _WIN32 + FD_SET((SOCKET)sockfd, &exceptfds); +#else + FD_SET(sockfd, &exceptfds); +#endif + + timeval tv; + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + int sel = select( +#ifdef _WIN32 + 0, +#else + sockfd + 1, +#endif + nullptr, &writefds, &exceptfds, &tv); + + if (sel > 0) { + // check for error on socket + int sock_err = 0; + socklen_t len = sizeof(sock_err); +#ifdef _WIN32 + int ret = getsockopt((SOCKET)sockfd, SOL_SOCKET, SO_ERROR, (char*)&sock_err, &len); +#else + int ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &sock_err, &len); +#endif +#ifdef _WIN32 + bool writable = FD_ISSET(static_cast(sockfd), &writefds) != 0; +#else + bool writable = FD_ISSET(sockfd, &writefds) != 0; +#endif + + if (ret == 0 && sock_err == 0 && writable) { + ok = true; + } + } + // else timeout or error -> try next addr + } + + // close socket +#ifdef _WIN32 + closesocket((SOCKET)sockfd); +#else + close(sockfd); +#endif + } + + freeaddrinfo(res); + return ok; +} + // NOTE: Due to the nature of getaddrinfo, this can block indefinitely. Not good. bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type) { #if PPSSPP_PLATFORM(SWITCH) diff --git a/Common/Net/Resolve.h b/Common/Net/Resolve.h index 09c2ab168c..15e89cc9f1 100644 --- a/Common/Net/Resolve.h +++ b/Common/Net/Resolve.h @@ -18,6 +18,8 @@ enum class DNSType { IPV6 = 2, }; +bool HostPortExists(const std::string& host, int port, int timeout_ms); + bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type = DNSType::ANY); void DNSResolveFree(addrinfo *res); bool GetLocalIP4List(std::vector& IP4s); diff --git a/Common/Render/AtlasGen.cpp b/Common/Render/AtlasGen.cpp index 7b968e29a3..241a4abd89 100644 --- a/Common/Render/AtlasGen.cpp +++ b/Common/Render/AtlasGen.cpp @@ -20,6 +20,7 @@ #include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Encoding/Utf8.h" +#include "Common/File/FileUtil.h" #include "Common/File/VFS/VFS.h" #include "Common/Render/AtlasGen.h" @@ -74,7 +75,7 @@ void Image::SaveZIM(const char *zim_name, int zim_format) { for (int y = 0; y < height(); y++) { memcpy(image_data + y * width() * 4, (dat.data() + y * w), width() * 4); } - FILE *f = fopen(zim_name, "wb"); + FILE *f = File::OpenCFile(Path(zim_name), "wb"); // SaveZIM takes ownership over image_data, there's no leak. ::SaveZIM(f, width(), height(), width() * 4, zim_format | ZIM_DITHER, image_data); fclose(f); diff --git a/Common/UI/IconCache.cpp b/Common/UI/IconCache.cpp index c1b6277f36..3c8747d4e6 100644 --- a/Common/UI/IconCache.cpp +++ b/Common/UI/IconCache.cpp @@ -6,6 +6,7 @@ #include "Common/Data/Format/PNGLoad.h" #include "Common/Log.h" #include "Common/GPU/thin3d.h" +#include "Common/File/FileUtil.h" #define ICON_CACHE_VERSION 1 #define MK_FOURCC(str) (str[0] | ((uint8_t)str[1] << 8) | ((uint8_t)str[2] << 16) | ((uint8_t)str[3] << 24)) @@ -88,7 +89,7 @@ bool IconCache::LoadFromFile(FILE *file) { // Check if we already have the entry somehow. if (cache_.find(key) != cache_.end()) { // Seek past the data and go to the next entry. - fseek(file, entryHeader.dataLen, SEEK_CUR); + File::Fseek(file, entryHeader.dataLen, SEEK_CUR); continue; } diff --git a/Common/UI/IconCache.h b/Common/UI/IconCache.h index 20f69c13c6..c93483a95b 100644 --- a/Common/UI/IconCache.h +++ b/Common/UI/IconCache.h @@ -6,6 +6,8 @@ #include #include +#include "Common/File/Path.h" + class UIContext; diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index 3f70c8a0da..fdce86a4bc 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -882,7 +882,7 @@ void AbstractChoiceWithValueDisplay::Draw(UIContext &dc) { } else { Choice::Draw(dc); - if (text_.empty() && !image_.isValid()) { + if (text_.empty() && valueText.empty() && !image_.isValid()) { // In this case we only display the image of the choice. Useful for small buttons spawning a popup. dc.Draw()->DrawImageRotated(ValueImage(), bounds_.centerX(), bounds_.centerY(), imgScale_, imgRot_, style.fgColor, imgFlipH_); return; diff --git a/Core/Config.cpp b/Core/Config.cpp index 88fc023b3d..8ed30e87d5 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1011,7 +1011,7 @@ static const ConfigSetting controlSettings[] = { static const ConfigSetting networkSettings[] = { ConfigSetting("EnableWlan", SETTING(g_Config, bEnableWlan), false, CfgFlag::PER_GAME), ConfigSetting("EnableAdhocServer", SETTING(g_Config, bEnableAdhocServer), false, CfgFlag::PER_GAME), - ConfigSetting("proAdhocServer", SETTING(g_Config, proAdhocServer), "socom.cc", CfgFlag::PER_GAME), + ConfigSetting("proAdhocServer", SETTING(g_Config, sProAdhocServer), "socom.cc", CfgFlag::PER_GAME), ConfigSetting("ServerHasRelay", SETTING(g_Config, bServerHasRelay), true, CfgFlag::PER_GAME), ConfigSetting("proAdhocServerList", SETTING(g_Config, proAdhocServerList), &defaultProAdhocServerList, CfgFlag::DEFAULT), ConfigSetting("PortOffset", SETTING(g_Config, iPortOffset), 10000, CfgFlag::PER_GAME), diff --git a/Core/Config.h b/Core/Config.h index 76469ef692..522cbdb6f2 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -538,7 +538,7 @@ public: // Networking bool bEnableAdhocServer; - std::string proAdhocServer; + std::string sProAdhocServer; bool bServerHasRelay; std::vector proAdhocServerList; std::string sInfrastructureDNSServer; diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp index 6d71345fd4..fd68959ddb 100644 --- a/Core/Debugger/SymbolMap.cpp +++ b/Core/Debugger/SymbolMap.cpp @@ -36,6 +36,8 @@ #include "zlib.h" +#include "ext/armips/Core/Assembler.h" + #include "Common/CommonTypes.h" #include "Common/Log.h" #include "Common/File/FileUtil.h" @@ -44,7 +46,6 @@ #include "Core/MemMap.h" #include "Core/Config.h" #include "Core/Debugger/SymbolMap.h" -#include "ext/armips/Core/Assembler.h" SymbolMap *g_symbolMap; @@ -228,30 +229,80 @@ bool SymbolMap::SaveSymbolMap(const Path &filename) const { std::string data; buf.TakeAll(&data); + FILE *file = File::OpenCFile(filename, "wb"); + if (file == nullptr) { + return false; + } if (g_Config.bCompressSymbols) { - // TODO: Wrap this in some nicer way. - gzFile f; - if (filename.Type() == PathType::CONTENT_URI) { - int fd = File::OpenFD(filename, File::OPEN_WRITE); - f = gzdopen(fd, "w9"); - if (f == Z_NULL) { - File::CloseFD(fd); - return false; - } - } else { - f = gzopen(filename.c_str(), "w9"); - if (f == Z_NULL) { - return false; - } + uInt out_size = 4096; + Bytef *out_data = (Bytef *)std::malloc(out_size); + if (out_data == nullptr) { + fclose(file); + return false; } - gzwrite(f, data.data(), (unsigned int)data.size()); - gzclose(f); + z_stream strm; + strm.zalloc = nullptr; + strm.zfree = nullptr; + strm.opaque = nullptr; + if (deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) { + std::free(out_data); + fclose(file); + return false; + } + strm.next_in = (Bytef *)data.data(); + strm.avail_in = data.size(); + strm.next_out = out_data; + strm.avail_out = out_size; + int flush = Z_NO_FLUSH; + for (;;) { + int status = deflate(&strm, flush); + switch (status) { + case Z_OK: + case Z_STREAM_END: + if (strm.avail_out != out_size) { + fwrite(out_data, 1, out_size - strm.avail_out, file); + } + break; + case Z_BUF_ERROR: + { + std::free(out_data); + uInt new_out_size = 2 * out_size; + if (new_out_size < out_size) { + deflateEnd(&strm); + fclose(file); + return false; + } + out_size = new_out_size; + out_data = (Bytef *)std::malloc(out_size); + if (out_data == nullptr) { + deflateEnd(&strm); + fclose(file); + return false; + } + } + break; + default: + deflateEnd(&strm); + std::free(out_data); + fclose(file); + return false; + } + if (status == Z_STREAM_END) { + break; + } + if (strm.avail_in == 0) { + flush = Z_FINISH; + } + strm.next_out = out_data; + strm.avail_out = out_size; + } + deflateEnd(&strm); + std::free(out_data); } else { // Just plain write it. - FILE *file = File::OpenCFile(filename, "wb"); fwrite(data.data(), 1, data.size(), file); - fclose(file); } + fclose(file); return true; } diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index 6a36add50d..ed40ef6ad5 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -37,11 +37,6 @@ #include #endif -#if PPSSPP_PLATFORM(SWITCH) -// Far from optimal, but I guess it works... -#define fseeko fseek -#endif - static const char * const CACHEFILE_MAGIC = "ppssppDC"; static const s64 SAFETY_FREE_DISK_SPACE = 768 * 1024 * 1024; // 768 MB // Aim to allow this many files cached at once. @@ -203,7 +198,7 @@ void DiskCachingFileLoaderCache::InitCache(const Path &filename) { void DiskCachingFileLoaderCache::ShutdownCache() { if (f_) { bool failed = false; - if (fseek(f_, sizeof(FileHeader), SEEK_SET) != 0) { + if (File::Fseek(f_, sizeof(FileHeader), SEEK_SET) != 0) { failed = true; } else if (fwrite(&index_[0], sizeof(BlockInfo), indexCount_, f_) != indexCount_) { failed = true; @@ -460,19 +455,11 @@ bool DiskCachingFileLoaderCache::ReadBlockData(u8 *dest, BlockInfo &info, size_t fflush(f_); bool failed = false; -#ifdef __ANDROID__ - if (lseek64(fd_, blockOffset, SEEK_SET) != blockOffset) { - failed = true; - } else if (read(fd_, dest + offset, size) != (ssize_t)size) { - failed = true; - } -#else - if (fseeko(f_, blockOffset, SEEK_SET) != 0) { + if (File::Fseek(f_, blockOffset, SEEK_SET) != 0) { failed = true; } else if (fread(dest + offset, size, 1, f_) != 1) { failed = true; } -#endif if (failed) { ERROR_LOG(Log::Loader, "Unable to read disk cache data entry."); @@ -488,19 +475,11 @@ void DiskCachingFileLoaderCache::WriteBlockData(BlockInfo &info, const u8 *src) s64 blockOffset = GetBlockOffset(info.block); bool failed = false; -#ifdef __ANDROID__ - if (lseek64(fd_, blockOffset, SEEK_SET) != blockOffset) { - failed = true; - } else if (write(fd_, src, blockSize_) != (ssize_t)blockSize_) { - failed = true; - } -#else - if (fseeko(f_, blockOffset, SEEK_SET) != 0) { + if (File::Fseek(f_, blockOffset, SEEK_SET) != 0) { failed = true; } else if (fwrite(src, blockSize_, 1, f_) != 1) { failed = true; } -#endif if (failed) { ERROR_LOG(Log::Loader, "Unable to write disk cache data entry."); @@ -516,7 +495,7 @@ void DiskCachingFileLoaderCache::WriteIndexData(u32 indexPos, BlockInfo &info) { u32 offset = (u32)sizeof(FileHeader) + indexPos * (u32)sizeof(BlockInfo); bool failed = false; - if (fseek(f_, offset, SEEK_SET) != 0) { + if (File::Fseek(f_, offset, SEEK_SET) != 0) { failed = true; } else if (fwrite(&info, sizeof(BlockInfo), 1, f_) != 1) { failed = true; @@ -553,11 +532,6 @@ bool DiskCachingFileLoaderCache::LoadCacheFile(const Path &path) { if (valid) { f_ = fp; -#ifdef __ANDROID__ - // Android NDK does not support 64-bit file I/O using C streams - fd_ = fileno(f_); -#endif - // Now let's load the index. blockSize_ = header.blockSize; maxBlocks_ = header.maxBlocks; @@ -572,7 +546,7 @@ bool DiskCachingFileLoaderCache::LoadCacheFile(const Path &path) { } void DiskCachingFileLoaderCache::LoadCacheIndex() { - if (fseek(f_, sizeof(FileHeader), SEEK_SET) != 0) { + if (File::Fseek(f_, sizeof(FileHeader), SEEK_SET) != 0) { CloseFileHandle(); return; } @@ -631,10 +605,6 @@ void DiskCachingFileLoaderCache::CreateCacheFile(const Path &path) { ERROR_LOG(Log::Loader, "Could not create disk cache file"); return; } -#ifdef __ANDROID__ - // Android NDK does not support 64-bit file I/O using C streams - fd_ = fileno(f_); -#endif blockSize_ = DEFAULT_BLOCK_SIZE; @@ -677,7 +647,7 @@ bool DiskCachingFileLoaderCache::LockCacheFile(bool lockStatus) { u32 offset = (u32)offsetof(FileHeader, flags); bool failed = false; - if (fseek(f_, offset, SEEK_SET) != 0) { + if (File::Fseek(f_, offset, SEEK_SET) != 0) { failed = true; } else if (fread(&flags_, sizeof(u32), 1, f_) != 1) { failed = true; @@ -704,7 +674,7 @@ bool DiskCachingFileLoaderCache::LockCacheFile(bool lockStatus) { flags_ &= ~FLAG_LOCKED; } - if (fseek(f_, offset, SEEK_SET) != 0) { + if (File::Fseek(f_, offset, SEEK_SET) != 0) { failed = true; } else if (fwrite(&flags_, sizeof(u32), 1, f_) != 1) { failed = true; diff --git a/Core/FileLoaders/LocalFileLoader.cpp b/Core/FileLoaders/LocalFileLoader.cpp index 89b0fe4ce9..280e3ec25f 100644 --- a/Core/FileLoaders/LocalFileLoader.cpp +++ b/Core/FileLoaders/LocalFileLoader.cpp @@ -62,15 +62,6 @@ LocalFileLoader::LocalFileLoader(const Path &filename) return; } -#if HAVE_LIBRETRO_VFS - isOpenedByFd_ = false; - handle_ = filestream_open(filename.c_str(), RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); - filestream_seek(handle_, 0, RETRO_VFS_SEEK_POSITION_END); - filesize_ = filestream_tell(handle_); - filestream_seek(handle_, 0, RETRO_VFS_SEEK_POSITION_START); - return; -#endif - #if PPSSPP_PLATFORM(ANDROID) && !defined(HAVE_LIBRETRO_VFS) if (filename.Type() == PathType::CONTENT_URI) { int fd = Android_OpenContentUriFd(filename.ToString(), Android_OpenContentUriMode::READ); @@ -87,7 +78,11 @@ LocalFileLoader::LocalFileLoader(const Path &filename) #endif #if defined(HAVE_LIBRETRO_VFS) - // Nothing to do here... + file_ = File::OpenCFile(filename, "rb"); + if (file_ == nullptr) { + return; + } + filesize_ = File::GetFileSize(file_); #elif !defined(_WIN32) fd_ = open(filename.c_str(), O_RDONLY | O_CLOEXEC); @@ -123,7 +118,9 @@ LocalFileLoader::LocalFileLoader(const Path &filename) LocalFileLoader::~LocalFileLoader() { #if defined(HAVE_LIBRETRO_VFS) - filestream_close(handle_); + if (file_ != nullptr) { + fclose(file_); + } #elif !defined(_WIN32) if (fd_ != -1) { close(fd_); @@ -138,8 +135,7 @@ LocalFileLoader::~LocalFileLoader() { bool LocalFileLoader::Exists() { // If we opened it for reading, it must exist. Done. #if defined(HAVE_LIBRETRO_VFS) - return handle_ != 0; - + return file_ != nullptr; #elif !defined(_WIN32) if (isOpenedByFd_) { // As an optimization, if we already tried and failed, quickly return. @@ -178,9 +174,9 @@ size_t LocalFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void } #if defined(HAVE_LIBRETRO_VFS) - std::lock_guard guard(readLock_); - filestream_seek(handle_, absolutePos, RETRO_VFS_SEEK_POSITION_START); - return filestream_read(handle_, data, bytes * count) / bytes; + std::lock_guard guard(readLock_); + File::Fseek(file_, absolutePos, SEEK_SET); + return fread(data, bytes, count, file_); #elif PPSSPP_PLATFORM(SWITCH) // Toolchain has no fancy IO API. We must lock. std::lock_guard guard(readLock_); diff --git a/Core/FileLoaders/LocalFileLoader.h b/Core/FileLoaders/LocalFileLoader.h index 7e1546a995..0d734f9206 100644 --- a/Core/FileLoaders/LocalFileLoader.h +++ b/Core/FileLoaders/LocalFileLoader.h @@ -24,15 +24,10 @@ #include "Common/StringUtils.h" #include "Core/Loaders.h" -#ifdef _WIN32 +#if defined(_WIN32) && !defined(HAVE_LIBRETRO_VFS) typedef void *HANDLE; #endif -#ifdef HAVE_LIBRETRO_VFS -#include -typedef RFILE* HANDLE; -#endif - class LocalFileLoader : public FileLoader { public: LocalFileLoader(const Path &filename); @@ -47,7 +42,9 @@ public: size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override; private: -#if !defined(_WIN32) && !defined(HAVE_LIBRETRO_VFS) +#ifdef HAVE_LIBRETRO_VFS + FILE *file_; +#elif !defined(_WIN32) void DetectSizeFd(); int fd_ = -1; #else diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index c5bd74a6c7..d8832457a4 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -155,7 +155,28 @@ bool DirectoryFileHandle::Open(const Path &basePath, std::string &fileName, File } //TODO: tests, should append seek to end of file? seeking in a file opened for append? -#if PPSSPP_PLATFORM(WINDOWS) +#ifdef HAVE_LIBRETRO_VFS + int flags = 0; + if (access & FILEACCESS_READ && access & FILEACCESS_WRITE) + flags = RETRO_VFS_FILE_ACCESS_READ_WRITE; + else if (access & FILEACCESS_WRITE) + flags = RETRO_VFS_FILE_ACCESS_WRITE; + else if (access & FILEACCESS_READ && access & FILEACCESS_CREATE) + flags = RETRO_VFS_FILE_ACCESS_READ_WRITE; + else + flags = RETRO_VFS_FILE_ACCESS_READ; + bool success = false; + if (!(access & FILEACCESS_CREATE)) { + hFile = filestream_open(fullName.c_str(), flags & RETRO_VFS_FILE_ACCESS_WRITE ? flags | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING : flags, RETRO_VFS_FILE_ACCESS_HINT_NONE); + success = hFile != nullptr; + } else if (!(access & FILEACCESS_EXCL)) { + hFile = filestream_open(fullName.c_str(), flags & RETRO_VFS_FILE_ACCESS_WRITE && File::Exists(fullName) ? flags | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING : flags, RETRO_VFS_FILE_ACCESS_HINT_NONE); + success = hFile != nullptr; + } else if (!File::Exists(fullName)) { + hFile = filestream_open(fullName.c_str(), flags & RETRO_VFS_FILE_ACCESS_WRITE ? flags | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING : flags, RETRO_VFS_FILE_ACCESS_HINT_NONE); + success = hFile != nullptr; + } +#elif PPSSPP_PLATFORM(WINDOWS) // Convert parameters to Windows permissions and access DWORD desired = 0; DWORD sharemode = 0; @@ -278,7 +299,19 @@ bool DirectoryFileHandle::Open(const Path &basePath, std::string &fileName, File DEBUG_LOG(Log::FileSystem, "Case may have been incorrect, second try opening %s (%s)", fullName.c_str(), fileName.c_str()); // And try again with the correct case this time -#if PPSSPP_PLATFORM(UWP) +#ifdef HAVE_LIBRETRO_VFS + success = false; + if (!(access & FILEACCESS_CREATE)) { + hFile = filestream_open(fullName.c_str(), flags & RETRO_VFS_FILE_ACCESS_WRITE ? flags | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING : flags, RETRO_VFS_FILE_ACCESS_HINT_NONE); + success = hFile != nullptr; + } else if (!(access & FILEACCESS_EXCL)) { + hFile = filestream_open(fullName.c_str(), flags & RETRO_VFS_FILE_ACCESS_WRITE && File::Exists(fullName) ? flags | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING : flags, RETRO_VFS_FILE_ACCESS_HINT_NONE); + success = hFile != nullptr; + } else if (!File::Exists(fullName)) { + hFile = filestream_open(fullName.c_str(), flags & RETRO_VFS_FILE_ACCESS_WRITE ? flags | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING : flags, RETRO_VFS_FILE_ACCESS_HINT_NONE); + success = hFile != nullptr; + } +#elif PPSSPP_PLATFORM(UWP) // Should never get here. #elif PPSSPP_PLATFORM(WINDOWS) // Unlikely to get here, heh. @@ -291,7 +324,7 @@ bool DirectoryFileHandle::Open(const Path &basePath, std::string &fileName, File } } -#if !PPSSPP_PLATFORM(WINDOWS) +#if !PPSSPP_PLATFORM(WINDOWS) && !defined(HAVE_LIBRETRO_VFS) if (success) { // Reject directories, even if we succeed in opening them. // TODO: Might want to do this stat first... @@ -337,7 +370,9 @@ size_t DirectoryFileHandle::Read(u8* pointer, s64 size) } } if (size > 0) { -#ifdef _WIN32 +#ifdef HAVE_LIBRETRO_VFS + bytesRead = fread(pointer, 1, size, hFile); +#elif defined(_WIN32) ::ReadFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesRead, 0); #else bytesRead = read(hFile, pointer, size); @@ -351,7 +386,9 @@ size_t DirectoryFileHandle::Write(const u8* pointer, s64 size) size_t bytesWritten = 0; bool diskFull = false; -#ifdef _WIN32 +#ifdef HAVE_LIBRETRO_VFS + bytesWritten = fwrite(pointer, 1, size, hFile); +#elif defined(_WIN32) BOOL success = ::WriteFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesWritten, 0); if (success == FALSE) { DWORD err = GetLastError(); @@ -406,7 +443,15 @@ size_t DirectoryFileHandle::Seek(s32 position, FileMove type) } size_t result; -#ifdef _WIN32 +#ifdef HAVE_LIBRETRO_VFS + int moveMethod = 0; + switch (type) { + case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break; + case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break; + case FILEMOVE_END: moveMethod = SEEK_END; break; + } + result = File::Fseektell(hFile, position, moveMethod); +#elif defined(_WIN32) DWORD moveMethod = 0; switch (type) { case FILEMOVE_BEGIN: moveMethod = FILE_BEGIN; break; @@ -434,7 +479,11 @@ size_t DirectoryFileHandle::Seek(s32 position, FileMove type) void DirectoryFileHandle::Close() { if (needsTrunc_ != -1) { -#ifdef _WIN32 +#ifdef HAVE_LIBRETRO_VFS + if (filestream_truncate(hFile, needsTrunc_) != 0) { + ERROR_LOG(Log::FileSystem, "Failed to truncate file to %d bytes", (int)needsTrunc_); + } +#elif defined(_WIN32) Seek((s32)needsTrunc_, FILEMOVE_BEGIN); if (SetEndOfFile(hFile) == 0) { ERROR_LOG(Log::FileSystem, "Failed to truncate file to %d bytes", (int)needsTrunc_); @@ -447,7 +496,10 @@ void DirectoryFileHandle::Close() { #endif } -#ifdef _WIN32 +#ifdef HAVE_LIBRETRO_VFS + if (hFile != nullptr) + fclose(hFile); +#elif defined(_WIN32) if (hFile != (HANDLE)-1) CloseHandle(hFile); #else @@ -598,7 +650,7 @@ int DirectoryFileSystem::OpenFile(std::string filename, FileAccess access, const } return err; } else { -#ifdef _WIN32 +#if defined(_WIN32) || defined(HAVE_LIBRETRO_VFS) if (access & FILEACCESS_APPEND) { entry.hFile.Seek(0, FILEMOVE_END); } diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index d3fb373781..083b15635c 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -24,7 +24,7 @@ #include "Common/File/Path.h" #include "Core/FileSystems/FileSystem.h" -#ifdef _WIN32 +#if defined(_WIN32) && !defined(HAVE_LIBRETRO_VFS) typedef void * HANDLE; #endif @@ -34,7 +34,9 @@ struct DirectoryFileHandle { SKIP_REPLAY, }; -#ifdef _WIN32 +#ifdef HAVE_LIBRETRO_VFS + FILE *hFile = nullptr; +#elif defined(_WIN32) HANDLE hFile = (HANDLE)-1; #else int hFile = -1; diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 97479bf316..75db5bf326 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1377,9 +1377,9 @@ int friendFinder() { addrinfo* resolved = nullptr; std::string err; g_adhocServerIP.in.sin_addr.s_addr = INADDR_NONE; - if (g_Config.bEnableWlan && !net::DNSResolve(g_Config.proAdhocServer, "", &resolved, err)) { - ERROR_LOG(Log::sceNet, "DNS Error Resolving %s\n", g_Config.proAdhocServer.c_str()); - g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("DNS Error Resolving")) + g_Config.proAdhocServer); + if (g_Config.bEnableWlan && !net::DNSResolve(g_Config.sProAdhocServer, "", &resolved, err)) { + ERROR_LOG(Log::sceNet, "DNS Error Resolving %s\n", g_Config.sProAdhocServer.c_str()); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("DNS Error Resolving")) + g_Config.sProAdhocServer); } if (resolved) { for (auto ptr = resolved; ptr != NULL; ptr = ptr->ai_next) { @@ -2262,7 +2262,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ sleep_ms(10, "pro-adhoc-socket-poll"); } if (!done) { - ERROR_LOG(Log::sceNet, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port)); + ERROR_LOG(Log::sceNet, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.sProAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port)); g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to connect to Adhoc Server")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(errorcode) + ")"); return iResult; } diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index 4560349fe0..4aca7822dc 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -36,9 +36,11 @@ #include "Common/File/FileUtil.h" #include "Common/TimeUtil.h" +#include "Common/Net/Resolve.h" #include "Core/Util/PortManager.h" #include "Core/Instance.h" #include "Core/Core.h" +#include "Core/Config.h" #include "Core/HLE/proAdhocServer.h" #ifdef _WIN32 @@ -344,6 +346,8 @@ static const db_productid default_productids[] = { { "ULES01432", "Full Metal Alchemist - Brotherhood" }, { "ULUS10490", "GTA Chinatown Wars" }, { "ULUS10160", "GTA Vice City Stories" }, + { "ULUS10041", "GTA Liberty City Stories" }, + { "ULES00151", "GTA Liberty City Stories" }, { "ULUS10210", "Ghost Rider" }, { "ULJS00237", "God Eater" }, { "NPJH50832", "God Eater 2" }, @@ -462,6 +466,7 @@ static const db_productid default_productids[] = { { "ULJM05151", "Yu-Gi-Oh! GX Tag Force" }, { "ULJM05373", "Yu-Gi-Oh! GX Tag Force 3" }, { "NPUG80086", "flOw" }, + // TODO? GTA 10160,beta,10041,00151 }; // Function Prototypes @@ -1660,7 +1665,13 @@ int proAdhocServerThread(int port) // (int argc, char * argv[]) // Result int result = 0; - INFO_LOG(Log::sceNet, "AdhocServer: Begin of AdhocServer Thread"); + if (net::HostPortExists(g_Config.sProAdhocServer, SERVER_PORT, 200)) { + INFO_LOG(Log::sceNet, "AdhocServer: Skipped starting because the server is already available"); + return 0; + } + else { + INFO_LOG(Log::sceNet, "AdhocServer: Begin of AdhocServer Thread"); + } // Create Signal Receiver for CTRL + C //signal(SIGINT, interrupt); @@ -1816,11 +1827,6 @@ int create_listen_socket(uint16_t port) local.sin_addr.s_addr = INADDR_ANY; local.sin_port = htons(port); - // Should only bind to specific IP for the 2nd or more instance of PPSSPP to prevent communication interference issue when sharing the same port. (ie. Capcom Classics Collection Remixed) - if (PPSSPP_ID > 1) { - local.sin_addr = g_localhostIP.in.sin_addr; - } - // Bind Local Address to Socket int bindresult = bind(fd, (struct sockaddr *)&local, sizeof(local)); diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 4ad3332a27..e35f1485da 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -521,7 +521,7 @@ void InitLocalhostIP() { g_localhostIP.in.sin_addr.s_addr = htonl(localIP); g_localhostIP.in.sin_port = 0; - std::string serverStr(StripSpaces(g_Config.proAdhocServer)); + std::string serverStr(StripSpaces(g_Config.sProAdhocServer)); isLocalServer = (!strcasecmp(serverStr.c_str(), "localhost") || serverStr.find("127.") == 0); } diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index 9530686c0f..7d8843f347 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -1182,10 +1182,26 @@ static int sceUtilityGamedataInstallAbort() { return hleLogDebug(Log::sceUtility, gamedataInstallDialog->Abort()); } +static const char *SystemParamToString(int param) { + switch (param) { + case PSP_SYSTEMPARAM_ID_STRING_NICKNAME: return "STRING_NICKNAME"; + case PSP_SYSTEMPARAM_ID_INT_ADHOC_CHANNEL: return "INT_ADHOC_CHANNEL"; + case PSP_SYSTEMPARAM_ID_INT_WLAN_POWERSAVE: return "INT_WLAN_POWERSAVE"; + case PSP_SYSTEMPARAM_ID_INT_DATE_FORMAT: return "INT_DATE_FORMAT"; + case PSP_SYSTEMPARAM_ID_INT_TIME_FORMAT: return "INT_TIME_FORMAT"; + case PSP_SYSTEMPARAM_ID_INT_TIMEZONE: return "INT_TIMEZONE"; + case PSP_SYSTEMPARAM_ID_INT_DAYLIGHTSAVINGS: return "INT_DAYLIGHTSAVINGS"; + case PSP_SYSTEMPARAM_ID_INT_LANGUAGE: return "INT_LANGUAGE"; + case PSP_SYSTEMPARAM_ID_INT_BUTTON_PREFERENCE: return "INT_BUTTON_PREFERENCE"; + case PSP_SYSTEMPARAM_ID_INT_LOCK_PARENTAL_LEVEL: return "INT_LOCK_PARENTAL_LEVEL"; + default: return "N/A"; + } +} + //TODO: should save to config file static u32 sceUtilitySetSystemParamString(u32 id, u32 strPtr) { - WARN_LOG_REPORT(Log::sceUtility, "sceUtilitySetSystemParamString(%i, %08x)", id, strPtr); + WARN_LOG_REPORT(Log::sceUtility, "sceUtilitySetSystemParamString(%s, %08x)", SystemParamToString(id), strPtr); return 0; } @@ -1194,7 +1210,6 @@ static u32 sceUtilityGetSystemParamString(u32 id, u32 destAddr, int destSize) { // TODO: What error code? return hleLogError(Log::sceUtility, -1); } - DEBUG_LOG(Log::sceUtility, "sceUtilityGetSystemParamString(%i, %08x, %i)", id, destAddr, destSize); char *buf = (char *)Memory::GetPointerWriteUnchecked(destAddr); switch (id) { case PSP_SYSTEMPARAM_ID_STRING_NICKNAME: @@ -1209,7 +1224,7 @@ static u32 sceUtilityGetSystemParamString(u32 id, u32 destAddr, int destSize) { return hleLogError(Log::sceUtility, SCE_ERROR_UTILITY_INVALID_SYSTEM_PARAM_ID); } - return hleLogDebug(Log::sceUtility, 0); + return hleLogDebug(Log::sceUtility, 0, "(%s)", SystemParamToString(id)); } static u32 sceUtilitySetSystemParamInt(u32 id, u32 value) { @@ -1226,7 +1241,7 @@ static u32 sceUtilitySetSystemParamInt(u32 id, u32 value) { // PSP can only set above int parameters return hleLogError(Log::sceUtility, SCE_ERROR_UTILITY_INVALID_SYSTEM_PARAM_ID); } - return hleLogDebug(Log::sceUtility, 0); + return hleLogDebug(Log::sceUtility, 0, "(%s)", SystemParamToString(id)); } static u32 sceUtilityGetSystemParamInt(u32 id, u32 destaddr) { @@ -1281,7 +1296,7 @@ static u32 sceUtilityGetSystemParamInt(u32 id, u32 destaddr) { } Memory::Write_U32(param, destaddr); - return hleLogInfo(Log::sceUtility, 0, "param: %08x", param); + return hleLogInfo(Log::sceUtility, 0, "(%s): %08x", SystemParamToString(id), param); } static u32 sceUtilityLoadNetModule(u32 module) { diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index 5a9ed1d8fc..231af74f87 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -319,39 +319,25 @@ bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &erro } // Close the return value with ZipClose (if non-null, of course). -struct zip *ZipOpenPath(const Path &fileName) { - int error = 0; - // Need to special case for content URI here, similar to OpenCFile. - struct zip *z; -#if PPSSPP_PLATFORM(ANDROID) - if (fileName.Type() == PathType::CONTENT_URI) { - int fd = File::OpenFD(fileName, File::OPEN_READ); - z = zip_fdopen(fd, 0, &error); - } else -#endif - { // continuation of above else in the ifdef - z = zip_open(fileName.c_str(), 0, &error); - } - - if (!z) { - ERROR_LOG(Log::HLE, "Failed to open ZIP file '%s', error code=%i", fileName.c_str(), error); +ZipContainer ZipOpenPath(const Path &fileName) { + ZipContainer z(fileName); + if (z == nullptr) { + ERROR_LOG(Log::HLE, "Failed to open ZIP file '%s'", fileName.c_str()); } return z; } -void ZipClose(struct zip *z) { - if (z) - zip_close(z); +void ZipClose(ZipContainer &z) { + z.close(); } bool DetectZipFileContents(const Path &fileName, ZipFileInfo *info) { - struct zip *z = ZipOpenPath(fileName); + ZipContainer z = ZipOpenPath(fileName); if (!z) { info->contents = ZipFileContents::UNKNOWN; return false; } DetectZipFileContents(z, info); - zip_close(z); return true; } @@ -408,7 +394,7 @@ static bool ZipExtractFileToMemory(struct zip *z, int fileIndex, std::string *da } } -void DetectZipFileContents(struct zip *z, ZipFileInfo *info) { +void DetectZipFileContents(zip_t *z, ZipFileInfo *info) { int numFiles = zip_get_num_files(z); _dbg_assert_(numFiles >= 0); diff --git a/Core/Loaders.h b/Core/Loaders.h index c60b19ed88..680da1431f 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -20,13 +20,9 @@ #include #include -#ifdef SHARED_LIBZIP -#include -#else -#include "ext/libzip/zip.h" -#endif #include "Common/CommonTypes.h" #include "Common/File/Path.h" +#include "Common/File/VFS/ZipFileReader.h" enum class IdentifiedFileType { ERROR_IDENTIFYING, @@ -191,8 +187,8 @@ struct ZipFileInfo { std::string contentName; }; -struct zip *ZipOpenPath(const Path &fileName); -void ZipClose(zip *z); +ZipContainer ZipOpenPath(const Path &fileName); +void ZipClose(ZipContainer &z); bool DetectZipFileContents(const Path &fileName, ZipFileInfo *info); -void DetectZipFileContents(struct zip *z, ZipFileInfo *info); +void DetectZipFileContents(zip_t *z, ZipFileInfo *info); diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index bd51f26061..27e62892a0 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -119,7 +119,7 @@ bool MIPSTracer::flush_to_file() { } INFO_LOG(Log::JIT, "Trace flushed, closing the file..."); - std::fclose(output); + fclose(output); clear(); return true; @@ -144,7 +144,7 @@ void MIPSTracer::flush_block_to_file(const TraceBlockInfo& block_info) { snprintf(buffer, sizeof(buffer), "0x%08x: ", addr); MIPSDisAsm(storage.read_asm(index), addr, buffer + prefix_size, sizeof(buffer) - prefix_size, true); - std::fprintf(output, "%s\n", buffer); + fprintf(output, "%s\n", buffer); } } diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 4c6313f6da..8aa91ad587 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -244,7 +244,7 @@ void GameManager::InstallZipContents(ZipFileTask task) { int error = 0; - struct zip *z = ZipOpenPath(task.fileName); + ZipContainer z = ZipOpenPath(task.fileName); if (!z) { g_OSD.RemoveProgressBar("install", false, 1.5f); SetInstallError(sy->T("Unable to open zip file")); @@ -281,22 +281,18 @@ void GameManager::InstallZipContents(ZipFileTask task) { } case ZipFileContents::TEXTURE_PACK: { - // InstallMemstickGame contains code to close z, and works for textures too. Path dest; if (DetectTexturePackDest(z, zipInfo.textureIniIndex, dest)) { INFO_LOG(Log::HLE, "Installing texture pack '%s' into '%s'", task.fileName.c_str(), dest.c_str()); File::CreateFullPath(dest); // Install as a zip file if textures.ini is in the root. Performs better on Android. if (zipInfo.stripChars == 0) { - success = InstallMemstickZip(z, task.fileName, dest / "textures.zip", zipInfo); + success = InstallMemstickZip(task.fileName, dest / "textures.zip", zipInfo); } else { // TODO: Can probably remove this, as we now put .nomedia in /TEXTURES directly. File::CreateEmptyFile(dest / ".nomedia"); success = ExtractZipContents(z, dest, zipInfo, true); } - } else { - zip_close(z); - z = nullptr; } break; } @@ -309,8 +305,6 @@ void GameManager::InstallZipContents(ZipFileTask task) { default: ERROR_LOG(Log::HLE, "File not a PSP game, no EBOOT.PBP found."); SetInstallError(sy->T("Not a PSP game")); - zip_close(z); - z = nullptr; break; } @@ -593,13 +587,10 @@ bool GameManager::ExtractZipContents(struct zip *z, const Path &dest, const ZipF } INFO_LOG(Log::HLE, "Unzipped %d files (%d bytes / %d).", info.numFiles, (int)bytesCopied, (int)allBytes); - zip_close(z); - z = nullptr; return true; bail: // We end up here if disk is full or couldn't write to storage for some other reason. - zip_close(z); // We don't delete the original in this case. Try to delete the files we created so far. for (size_t i = 0; i < createdFiles.size(); i++) { File::Delete(createdFiles[i]); @@ -611,16 +602,12 @@ bail: return false; } -bool GameManager::InstallMemstickZip(struct zip *z, const Path &zipfile, const Path &dest, const ZipFileInfo &info) { +bool GameManager::InstallMemstickZip(const Path &zipfile, const Path &dest, const ZipFileInfo &info) { size_t allBytes = 0; size_t bytesCopied = 0; auto sy = GetI18NCategory(I18NCat::SYSTEM); - // We don't need the zip anymore, as we're going to copy it as-is. - zip_close(z); - z = nullptr; - // Not using File::Copy() so we can report progress. FILE *inf = File::OpenCFile(zipfile, "rb"); if (!inf) @@ -705,10 +692,8 @@ bool GameManager::InstallZippedISO(struct zip *z, int isoFileIndex, const Path & INFO_LOG(Log::IO, "Successfully unzipped ISO file to '%s'", outputISOFilename.c_str()); success = true; } - zip_close(z); g_OSD.RemoveProgressBar("install", success, 0.5f); - z = 0; installProgress_ = 1.0f; InstallDone(); ResetInstallError(); diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 3bc9dbf70b..67c6be6084 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -92,7 +92,7 @@ private: void InstallZipContents(ZipFileTask task); bool ExtractZipContents(struct zip *z, const Path &dest, const ZipFileInfo &info, bool allowRoot); - bool InstallMemstickZip(struct zip *z, const Path &zipFile, const Path &dest, const ZipFileInfo &info); + bool InstallMemstickZip(const Path &zipFile, const Path &dest, const ZipFileInfo &info); bool InstallZippedISO(struct zip *z, int isoFileIndex, const Path &destDir); void UninstallGame(const std::string &name); diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index face815634..b3d4e4f02b 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -249,7 +249,7 @@ static void DiscHandler(const http::ServerRequest &request, const Path &filename } FILE *fp = File::OpenCFile(filename, "rb"); - if (!fp || fseek(fp, begin, SEEK_SET) != 0) { + if (!fp || File::Fseek(fp, begin, SEEK_SET) != 0) { request.WriteHttpResponseHeader("1.0", 500, -1, "text/plain"); request.Out()->Push("File access failed."); if (fp) { diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index b80a3d2351..91fccce988 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -659,7 +659,7 @@ void PipelineManagerVulkan::SavePipelineCache(FILE *file, bool saveRawPipelineCa NOTICE_LOG(Log::G3D, "Saved Vulkan pipeline cache (%d bytes).", (int)size); } - size_t seekPosOnFailure = ftell(file); + int64_t seekPosOnFailure = File::Ftell(file); bool failed = false; bool writeFailed = false; @@ -711,7 +711,7 @@ void PipelineManagerVulkan::SavePipelineCache(FILE *file, bool saveRawPipelineCa ERROR_LOG(Log::G3D, "Failed to write pipeline cache, some shader was missing"); // Write a zero in the right place so it doesn't try to load the pipelines next time. size = 0; - fseek(file, (long)seekPosOnFailure, SEEK_SET); + File::Fseek(file, seekPosOnFailure, SEEK_SET); writeFailed = fwrite(&size, sizeof(size), 1, file) != 1; if (writeFailed) { ERROR_LOG(Log::G3D, "Failed to write pipeline cache, disk full?"); diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index b1b83664e0..f96b3d50b4 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -30,6 +30,8 @@ #include "GPU/Vulkan/VulkanQueueRunner.h" #include "GPU/Vulkan/VulkanRenderManager.h" +#include "Common/File/FileUtil.h" + struct VKRGraphicsPipeline; class VulkanRenderManager; class VulkanContext; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index c1424dc985..6f28e58495 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -506,10 +506,10 @@ struct VulkanCacheHeader { bool ShaderManagerVulkan::LoadCacheFlags(FILE *f, DrawEngineVulkan *drawEngine) { VulkanCacheHeader header{}; - long pos = ftell(f); + int64_t pos = File::Ftell(f); bool success = fread(&header, sizeof(header), 1, f) == 1; // We'll read it again later, this is just to check the flags. - success = success && fseek(f, pos, SEEK_SET) == 0; + success = success && File::Fseek(f, pos, SEEK_SET) == 0; if (!success || header.magic != CACHE_HEADER_MAGIC) { WARN_LOG(Log::G3D, "Shader cache magic mismatch"); return false; diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index fd14c235c0..f49f2de125 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -32,6 +32,8 @@ #include "Common/Math/lin/matrix4x4.h" #include "GPU/Common/ShaderUniforms.h" +#include "Common/File/FileUtil.h" + class VulkanContext; class DrawEngineVulkan; class VulkanPushPool; diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index d950f3cd11..bd792b1112 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -39,7 +39,7 @@ class SingleControlMapper; class ControlMappingScreen : public UITwoPaneBaseDialogScreen { public: - ControlMappingScreen(const Path &gamePath) : UITwoPaneBaseDialogScreen(gamePath, TwoPaneFlags::SettingsInContextMenu | TwoPaneFlags::ContentsCanScroll) { + ControlMappingScreen(const Path &gamePath) : UITwoPaneBaseDialogScreen(gamePath, TwoPaneFlags::SettingsInContextMenu | TwoPaneFlags::ContentsCanScroll | TwoPaneFlags::NoTopbarInLandscape) { categoryToggles_[0] = true; categoryToggles_[1] = true; categoryToggles_[2] = true; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 93681e40a2..0d70ecc67f 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1659,7 +1659,8 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { Draw::BackendState state = draw->GetCurrentBackendState(); if (state.valid) { - _dbg_assert_msg_(state.passes >= 1, "skipB: %d sw: %d mode: %d back: %d tag: %s behi: %d", (int)skipBufferEffects, (int)g_Config.bSoftwareRendering, (int)mode, (int)g_Config.iGPUBackend, screenManager()->topScreen()->tag(), (int)g_Config.bRunBehindPauseMenu); + // The below can trigger when switching from skip-buffer-effects. We don't really care anymore... + // _dbg_assert_msg_(state.passes >= 1, "skipB: %d sw: %d mode: %d back: %d tag: %s behi: %d", (int)skipBufferEffects, (int)g_Config.bSoftwareRendering, (int)mode, (int)g_Config.iGPUBackend, screenManager()->topScreen()->tag(), (int)g_Config.bRunBehindPauseMenu); // Workaround any remaining bugs like this. if (state.passes == 0) { draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, }, "EmuScreen_SafeFallback"); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 8293a738f7..6e3fb79447 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1015,8 +1015,8 @@ void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSetti networkingSettings->Add(new ItemHeader(n->T("AdHoc server"))); networkingSettings->Add(new CheckBox(&g_Config.bEnableAdhocServer, n->T("Enable built-in PRO Adhoc Server", "Enable built-in PRO Adhoc Server"))); - networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address"), I18NCat::NONE))->OnClick.Add([=](UI::EventParams &) { - screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, &g_Config.proAdhocServerList, n->T("proAdhocServer Address:"))); + networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sProAdhocServer, n->T("Change proAdhocServer Address"), I18NCat::NONE))->OnClick.Add([=](UI::EventParams &) { + screenManager()->push(new HostnameSelectScreen(&g_Config.sProAdhocServer, &g_Config.proAdhocServerList, n->T("proAdhocServer Address:"))); }); networkingSettings->Add(new SettingHint(n->T("Change proAdhocServer address hint"))); networkingSettings->Add(new CheckBox(&g_Config.bServerHasRelay, n->T("PRO Adhoc packet relay available", "PRO Adhoc packet relay available")))->SetEnabled(!PSP_IsInited()); diff --git a/UI/InstallZipScreen.cpp b/UI/InstallZipScreen.cpp index f39cc88bb9..1330eec76c 100644 --- a/UI/InstallZipScreen.cpp +++ b/UI/InstallZipScreen.cpp @@ -35,7 +35,7 @@ InstallZipScreen::InstallZipScreen(const Path &zipPath) : UITwoPaneBaseDialogScreen(Path(), TwoPaneFlags::SettingsToTheRight | TwoPaneFlags::ContentsCanScroll), zipPath_(zipPath) { g_GameManager.ResetInstallError(); - struct zip *zipFile = ZipOpenPath(zipPath_); + ZipContainer zipFile = ZipOpenPath(zipPath_); if (zipFile) { DetectZipFileContents(zipFile, &zipFileInfo_); // Even if this fails, it sets zipInfo->contents. ZipClose(zipFile); @@ -177,7 +177,7 @@ void InstallZipScreen::CreateContentViews(UI::ViewGroup *parent) { leftColumn->Add(new TextView(zipFileInfo_.gameTitle + ": " + zipFileInfo_.savedataDir)); Path savedataDir = GetSysDirectory(DIRECTORY_SAVEDATA); - struct zip *zipFile = ZipOpenPath(zipPath_); + ZipContainer zipFile = ZipOpenPath(zipPath_); bool overwrite = !CanExtractWithoutOverwrite(zipFile, savedataDir, 50); ZipClose(zipFile); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 875e92e890..c7958f84b4 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1138,44 +1138,76 @@ GameBrowser *MainScreen::CreateBrowserTab(const Path &path, std::string_view tit return gameBrowser; } -UI::ViewGroup *MainScreen::CreateLogoView(bool portrait, UI::LayoutParams *layoutParams) { - using namespace UI; - AnchorLayout *logos = new AnchorLayout(layoutParams); - if (System_GetPropertyBool(SYSPROP_APP_GOLD)) { - logos->Add(new ImageView(ImageID("I_ICON_GOLD"), "", IS_DEFAULT, new AnchorLayoutParams(64, 64, 0, 0, NONE, NONE))); - } else { - logos->Add(new ImageView(ImageID("I_ICON"), "", IS_DEFAULT, new AnchorLayoutParams(64, 64, 0, 0, NONE, NONE))); - } - logos->Add(new ImageView(ImageID("I_LOGO"), "PPSSPP", IS_DEFAULT, new AnchorLayoutParams(180, 64, 68, 2, NONE, NONE))); +class LogoView : public UI::AnchorLayout { +public: + LogoView(bool portrait, UI::LayoutParams *layoutParams) : UI::AnchorLayout(layoutParams), portrait_(portrait) {} + void Draw(UIContext &dc) override { + using namespace UI; + UI::AnchorLayout::Draw(dc); - std::string versionString = PPSSPP_GIT_VERSION; - // Strip the 'v' from the displayed version, and shorten the commit hash. - if (versionString.size() > 2) { - if (versionString[0] == 'v' && isdigit(versionString[1])) { - versionString = versionString.substr(1); + const AtlasImage *iconImg = dc.Draw()->GetAtlas()->getImage(GetIconID()); + const AtlasImage *logoImg = dc.Draw()->GetAtlas()->getImage(ImageID("I_LOGO")); + if (!iconImg) { + return; } - if (CountChar(versionString, '-') == 2) { - // Shorten the commit hash. - size_t cutPos = versionString.find_last_of('-') + 8; - versionString = versionString.substr(0, std::min(cutPos, versionString.size())); + + dc.Draw()->DrawImage(GetIconID(), bounds_.x, bounds_.y, 1.0f); + + if (bounds_.w < iconImg->w + logoImg->w + 36) { + return; } + + dc.Draw()->DrawImage(ImageID("I_LOGO"), bounds_.x + iconImg->w + 8, bounds_.y + 4, 1.0f); + + std::string versionString = PPSSPP_GIT_VERSION; + // Strip the 'v' from the displayed version, and shorten the commit hash. + if (versionString.size() > 2) { + if (versionString[0] == 'v' && isdigit(versionString[1])) { + versionString = versionString.substr(1); + } + if (CountChar(versionString, '-') == 2) { + // Shorten the commit hash. + size_t cutPos = versionString.find_last_of('-') + 8; + versionString = versionString.substr(0, std::min(cutPos, versionString.size())); + } + } + dc.Flush(); + + const bool tiny = versionString.size() > 10; + + const FontStyle *style = GetTextStyle(dc, tiny ? TextSize::Tiny : TextSize::Small); + dc.SetFontStyle(*style); + dc.DrawText(versionString, + bounds_.x + iconImg->w + 8, + bounds_.y + logoImg->h + (tiny ? 8 : 6), + dc.GetTheme().itemStyle.fgColor); + dc.SetFontStyle(dc.GetTheme().uiFont); } - ClickableTextView *ver = logos->Add(new ClickableTextView(versionString, new AnchorLayoutParams(68, NONE, NONE, 0))); - ver->SetSmall(true); - ver->SetClip(false); + void GetContentDimensions(const UIContext &dc, float &w, float &h) const override { + const AtlasImage *iconImg = dc.Draw()->GetAtlas()->getImage(GetIconID()); + w = iconImg->w; + h = iconImg->h; + } - // Only allow copying the version if it looks like a git version string. 1.19 for example is not really necessary to be able to copy/paste. - if (!portrait && strchr(PPSSPP_GIT_VERSION, '-')) { - ver->OnClick.Add([](UI::EventParams &e) { + bool Touch(const TouchInput &touch) override { + bool retval = UI::AnchorLayout::Touch(touch); + if (!portrait_ && (touch.flags & TouchInputFlags::DOWN) && bounds_.Contains(touch.x, touch.y) && touch.y >= bounds_.y2() - 20) { auto di = GetI18NCategory(I18NCat::DIALOG); System_CopyStringToClipboard(PPSSPP_GIT_VERSION); - g_OSD.Show(OSDType::MESSAGE_INFO, ApplySafeSubstitutions(di->T("Copied to clipboard: %1"), PPSSPP_GIT_VERSION)); - }); + g_OSD.Show(OSDType::MESSAGE_INFO, ApplySafeSubstitutions(di->T("Copied to clipboard: %1"), PPSSPP_GIT_VERSION), 1.0f, "copyToClip"); + return true; + } + return retval; } - return logos; -} +private: + ImageID GetIconID() const { + return System_GetPropertyBool(SYSPROP_APP_GOLD) ? ImageID("I_ICON_GOLD") : ImageID("I_ICON"); + } + + const bool portrait_; +}; void MainScreen::CreateMainButtons(UI::ViewGroup *parent, bool portrait) { using namespace UI; @@ -1318,7 +1350,7 @@ void MainScreen::CreateViews() { if (vertical) { LinearLayout *header = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(8, 8, 8, 16))); header->SetSpacing(5.0f); - header->Add(CreateLogoView(true, new LinearLayoutParams(WRAP_CONTENT, 80.0f, false))); + header->Add(new LogoView(true, new LinearLayoutParams(1.0f))); LinearLayout *buttonGroup = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1.0f, UI::Gravity::G_VCENTER)); @@ -1337,7 +1369,7 @@ void MainScreen::CreateViews() { ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); rightColumnItems->SetSpacing(0.0f); - ViewGroup *logo = CreateLogoView(false, new LinearLayoutParams(FILL_PARENT, 80.0f)); + ViewGroup *logo = new LogoView(false, new LinearLayoutParams(FILL_PARENT, 80.0f)); #if !defined(MOBILE_DEVICE) auto gr = GetI18NCategory(I18NCat::GRAPHICS); ImageID icon(g_Config.UseFullScreen() ? "I_RESTORE" : "I_FULLSCREEN"); diff --git a/UI/MainScreen.h b/UI/MainScreen.h index d10bafd3f4..f27ee7d73f 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -140,7 +140,6 @@ protected: void CreateViews() override; void CreateRecentTab(); GameBrowser *CreateBrowserTab(const Path &path, std::string_view title, std::string_view howToTitle, std::string_view howToUri, BrowseFlags browseFlags, bool *bGridView, float *scrollPos); - UI::ViewGroup *CreateLogoView(bool portrait, UI::LayoutParams *layoutParams); void CreateMainButtons(UI::ViewGroup *parent, bool vertical); void DrawBackground(UIContext &dc) override; diff --git a/UI/SimpleDialogScreen.cpp b/UI/SimpleDialogScreen.cpp index 6e45eec487..39b9d2f0da 100644 --- a/UI/SimpleDialogScreen.cpp +++ b/UI/SimpleDialogScreen.cpp @@ -50,9 +50,11 @@ void UITwoPaneBaseDialogScreen::CreateViews() { BeforeCreateViews(); - auto createContentViews = [this](UI::ViewGroup *parent) { + auto createContentViews = [this, portrait](UI::ViewGroup *parent) { if (flags_ & TwoPaneFlags::ContentsCanScroll) { - Margins margins(8, 8, 8, 0); + int scrollMargin = (!portrait && (flags_ & TwoPaneFlags::NoTopbarInLandscape)) ? 0 : 8; + + Margins margins(8, scrollMargin, 8, 0); if (flags_ & TwoPaneFlags::SettingsToTheRight) { // If settings are in context menu, we want to avoid double margins on the sides. margins.left = 0; @@ -119,10 +121,13 @@ void UITwoPaneBaseDialogScreen::CreateViews() { if (flags_ & TwoPaneFlags::CustomContextMenu) { topBarFlags |= TopBarFlags::ContextMenuButton; } - TopBar *topBar = root->Add(new TopBar(*screenManager()->getUIContext(), topBarFlags, title)); + TopBar *topBar = nullptr; + if (!(flags_ & TwoPaneFlags::NoTopbarInLandscape)) { + topBar = root->Add(new TopBar(*screenManager()->getUIContext(), topBarFlags, title)); + } root->SetSpacing(0); - if (flags_ & TwoPaneFlags::CustomContextMenu) { + if ((flags_ & TwoPaneFlags::CustomContextMenu) && topBar) { View *menuButton = topBar->GetContextMenuButton(); topBar->OnContextMenuClick.Add([this, menuButton](UI::EventParams &e) { this->screenManager()->push(new PopupCallbackScreen([this](UI::ViewGroup *parent) { @@ -137,9 +142,14 @@ void UITwoPaneBaseDialogScreen::CreateViews() { ScrollView *settingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(SettingsWidth(), FILL_PARENT, 0.0f, Margins(0, 8, 0, 0))); LinearLayout *settingsPane = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, Margins(0, 8, 0, 0))); settingsPane->SetSpacing(0.0f); + + if (flags_ & TwoPaneFlags::NoTopbarInLandscape) { + // We still need a back button. + settingsPane->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK")))->OnClick.Handle(this, &UIScreen::OnBack); + settingsPane->Add(new Spacer(8.0f)); + } CreateSettingsViews(settingsPane); // settingsPane->Add(new BorderView(BORDER_BOTTOM, BorderStyle::HEADER_FG, 2.0f, new LayoutParams(FILL_PARENT, 40.0f))); - // settingsPane->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK")))->OnClick.Handle(this, &UIScreen::OnBack); settingsScroll->Add(settingsPane); if (flags_ & TwoPaneFlags::SettingsToTheRight) { diff --git a/UI/SimpleDialogScreen.h b/UI/SimpleDialogScreen.h index dce72ebd4d..404689993d 100644 --- a/UI/SimpleDialogScreen.h +++ b/UI/SimpleDialogScreen.h @@ -38,6 +38,7 @@ enum class TwoPaneFlags { SettingsCanScroll = 4, ContentsCanScroll = 8, CustomContextMenu = 16, + NoTopbarInLandscape = 32, }; ENUM_CLASS_BITOPS(TwoPaneFlags); diff --git a/android/build.gradle.kts b/android/build.gradle.kts index ed37db6174..cb00660b4d 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -160,6 +160,7 @@ android { } ndk { abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86_64")) + debugSymbolLevel = "FULL" } } create("gold") { @@ -179,6 +180,7 @@ android { } ndk { abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86_64")) + debugSymbolLevel = "FULL" } } create("legacy") { @@ -199,6 +201,7 @@ android { } ndk { abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a")) + debugSymbolLevel = "FULL" } } create("vr") { diff --git a/libretro/CMakeLists.txt b/libretro/CMakeLists.txt index 7ebc7a6e18..d304b4dba4 100644 --- a/libretro/CMakeLists.txt +++ b/libretro/CMakeLists.txt @@ -12,6 +12,19 @@ if(WIN32) LibretroD3D11Context.cpp) endif() +set(LIBRETRO_SRCS ${LIBRETRO_SRCS} + libretro-common/compat/compat_posix_string.c + libretro-common/compat/fopen_utf8.c + libretro-common/compat/compat_strl.c + libretro-common/encodings/encoding_utf.c + libretro-common/file/file_path.c + libretro-common/file/file_path_io.c + libretro-common/streams/file_stream.c + libretro-common/streams/file_stream_transforms.c + libretro-common/string/stdstring.c + libretro-common/time/rtime.c + libretro-common/vfs/vfs_implementation.c) + include_directories(libretro) add_library(ppsspp_libretro SHARED ${LIBRETRO_SRCS}) @@ -20,6 +33,7 @@ set_target_properties(ppsspp_libretro PROPERTIES PREFIX "") if(ANDROID) set_target_properties(ppsspp_libretro PROPERTIES SUFFIX "_android.so") endif() +target_include_directories(ppsspp_libretro PRIVATE libretro-common/include) if(NOT MSVC) if (APPLE OR IOS) diff --git a/libretro/LibretroD3D11Context.h b/libretro/LibretroD3D11Context.h index 2fd51dfdf6..b47755876c 100644 --- a/libretro/LibretroD3D11Context.h +++ b/libretro/LibretroD3D11Context.h @@ -1,12 +1,12 @@ #pragma once #define HAVE_D3D11 -#include "libretro/libretro_d3d.h" +#include #include "libretro/LibretroGraphicsContext.h" class LibretroD3D11Context : public LibretroHWRenderContext { public: - LibretroD3D11Context() : LibretroHWRenderContext(RETRO_HW_CONTEXT_DIRECT3D, 11) {} + LibretroD3D11Context() : LibretroHWRenderContext(RETRO_HW_CONTEXT_D3D11, 11) {} bool Init() override; void SwapBuffers() override; diff --git a/libretro/LibretroFileStreamTransforms.h b/libretro/LibretroFileStreamTransforms.h new file mode 100644 index 0000000000..fee177f327 --- /dev/null +++ b/libretro/LibretroFileStreamTransforms.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#undef fopen +#undef fseek +#undef ftell diff --git a/libretro/LibretroGraphicsContext.cpp b/libretro/LibretroGraphicsContext.cpp index a5d6f2931e..cc7a272e27 100644 --- a/libretro/LibretroGraphicsContext.cpp +++ b/libretro/LibretroGraphicsContext.cpp @@ -133,7 +133,7 @@ LibretroGraphicsContext *LibretroGraphicsContext::CreateGraphicsContext() { #endif #ifdef _WIN32 - if (preferred == RETRO_HW_CONTEXT_DUMMY || preferred == RETRO_HW_CONTEXT_DIRECT3D) { + if (preferred == RETRO_HW_CONTEXT_DUMMY || preferred == RETRO_HW_CONTEXT_D3D11) { ctx = new LibretroD3D11Context(); if (ctx->Init()) { diff --git a/libretro/LibretroGraphicsContext.h b/libretro/LibretroGraphicsContext.h index 26c6c199d0..5baad23a68 100644 --- a/libretro/LibretroGraphicsContext.h +++ b/libretro/LibretroGraphicsContext.h @@ -1,7 +1,7 @@ #pragma once #include -#include "libretro/libretro.h" +#include #include "Common/GraphicsContext.h" #include "Common/GPU/thin3d_create.h" diff --git a/libretro/LibretroVulkanContext.cpp b/libretro/LibretroVulkanContext.cpp index a4c63147d3..6c1b423ad4 100644 --- a/libretro/LibretroVulkanContext.cpp +++ b/libretro/LibretroVulkanContext.cpp @@ -10,9 +10,11 @@ #include "Common/Data/Text/Parsers.h" #include "libretro/LibretroVulkanContext.h" -#include "libretro/libretro_vulkan.h" +#include #include +#undef fflush + static VulkanContext *vk; void vk_libretro_init(VkInstance instance, VkPhysicalDevice gpu, VkSurfaceKHR surface, PFN_vkGetInstanceProcAddr get_instance_proc_addr, const char **required_device_extensions, unsigned num_required_device_extensions, const char **required_device_layers, unsigned num_required_device_layers, const VkPhysicalDeviceFeatures *required_features); diff --git a/libretro/Makefile.common b/libretro/Makefile.common index f0489482fc..1c90e1e18d 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -1,3 +1,5 @@ +HAVE_LIBRETRO_VFS ?= 1 + FFMPEGDIR = $(CORE_DIR)/ffmpeg LIBRETRODIR = $(CORE_DIR)/libretro COREDIR = $(CORE_DIR)/Core @@ -1137,13 +1139,13 @@ SOURCES_C += \ endif ifeq ($(HAVE_LIBRETRO_VFS), 1) -DEFINES += -DHAVE_LIBRETRO_VFS -INCFLAGS += -I$(LIBRETROCOMMONDIR)/include +COREFLAGS += -DHAVE_LIBRETRO_VFS SOURCES_C += $(LIBRETROCOMMONDIR)/compat/compat_posix_string.c \ $(LIBRETROCOMMONDIR)/compat/fopen_utf8.c \ - $(LIBRETROCOMMONDIR)/encodings/encoding_utf.c \ $(LIBRETROCOMMONDIR)/compat/compat_strl.c \ + $(LIBRETROCOMMONDIR)/encodings/encoding_utf.c \ $(LIBRETROCOMMONDIR)/file/file_path.c \ + $(LIBRETROCOMMONDIR)/file/file_path_io.c \ $(LIBRETROCOMMONDIR)/streams/file_stream.c \ $(LIBRETROCOMMONDIR)/streams/file_stream_transforms.c \ $(LIBRETROCOMMONDIR)/string/stdstring.c \ @@ -1151,6 +1153,8 @@ SOURCES_C += $(LIBRETROCOMMONDIR)/compat/compat_posix_string.c \ $(LIBRETROCOMMONDIR)/vfs/vfs_implementation.c endif +INCFLAGS += -I$(LIBRETROCOMMONDIR)/include + GIT_VERSION_SRC = $(CORE_DIR)/git-version.cpp GIT_VERSION := $(shell git describe --always || echo v-1.unknown) GIT_VERSION_NO_UPDATE = $(findstring 1,$(shell grep -s PPSSPP_GIT_VERSION_NO_UPDATE $(GIT_VERSION_SRC))) diff --git a/libretro/jni/Android.mk b/libretro/jni/Android.mk index f37ba11548..e17e48d363 100644 --- a/libretro/jni/Android.mk +++ b/libretro/jni/Android.mk @@ -10,7 +10,6 @@ CORE_DIR := ../.. FFMPEGDIR := $(CORE_DIR)/ffmpeg FFMPEGLIBS += libavformat libavcodec libavutil libswresample libswscale WITH_DYNAREC := 1 -HAVE_LIBRETRO_VFS := 1 ifeq ($(TARGET_ARCH),arm64) COREFLAGS += -D_ARCH_64 diff --git a/libretro/libretro-common b/libretro/libretro-common index 2a4b12d1a3..76a3d54feb 160000 --- a/libretro/libretro-common +++ b/libretro/libretro-common @@ -1 +1 @@ -Subproject commit 2a4b12d1a3aa69f521b8322ef48b23a4a585b38c +Subproject commit 76a3d54feb0ee0ce9d59b90aa24694f3782063d3 diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 8b6da35538..1c506b5380 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -46,7 +46,7 @@ #include "UI/AudioCommon.h" -#include "libretro/libretro.h" +#include #include "libretro/LibretroGraphicsContext.h" #include "libretro/libretro_core_options.h" @@ -401,11 +401,11 @@ void retro_set_environment(retro_environment_t cb) update_display_cb.callback = set_variable_visibility; environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK, &update_display_cb); - #ifdef HAVE_LIBRETRO_VFS - struct retro_vfs_interface_info vfs_iface_info { 1, nullptr }; - if (cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info)) - filestream_vfs_init(&vfs_iface_info); - #endif +#ifdef HAVE_LIBRETRO_VFS + struct retro_vfs_interface_info vfs_iface_info { 2, nullptr }; + if (cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info)) + File::InitLibretroVFS(&vfs_iface_info); +#endif } static int get_language_auto(void) @@ -1026,7 +1026,7 @@ static void check_variables(CoreParameter &coreParam) char key[64] = {0}; var.key = key; g_Config.sMACAddress = ""; - g_Config.proAdhocServer = ""; + g_Config.sProAdhocServer = ""; for (int i = 0; i < 12; i++) { snprintf(key, sizeof(key), "ppsspp_change_mac_address%02d", i + 1); @@ -1060,13 +1060,13 @@ static void check_variables(CoreParameter &coreParam) if (changeProAdhocServer == "IP address") { - g_Config.proAdhocServer = ""; + g_Config.sProAdhocServer = ""; bool leadingZero = true; for (int i = 0; i < 12; i++) { if (i && i % 3 == 0) { - g_Config.proAdhocServer += '.'; + g_Config.sProAdhocServer += '.'; leadingZero = true; } @@ -1075,11 +1075,11 @@ static void check_variables(CoreParameter &coreParam) leadingZero = false; // We are either non-zero or the last digit of a byte if (! leadingZero) - g_Config.proAdhocServer += static_cast('0' + addressPt); + g_Config.sProAdhocServer += static_cast('0' + addressPt); } } else - g_Config.proAdhocServer = changeProAdhocServer; + g_Config.sProAdhocServer = changeProAdhocServer; g_Config.bTexHardwareScaling = g_Config.sTextureShaderName != "Off"; @@ -1473,7 +1473,7 @@ static void retro_check_backend(void) else if (!strcmp(var.value, "vulkan")) backend = RETRO_HW_CONTEXT_VULKAN; else if (!strcmp(var.value, "d3d11")) - backend = RETRO_HW_CONTEXT_DIRECT3D; + backend = RETRO_HW_CONTEXT_D3D11; else if (!strcmp(var.value, "none")) backend = RETRO_HW_CONTEXT_NONE; } @@ -1857,10 +1857,11 @@ void retro_cheat_reset(void) { Path file=cheatEngine->CheatFilename(); // Output cheats to cheat file - std::ofstream outFile; - outFile.open(file.c_str()); - outFile << "_S " << g_paramSFO.GetDiscID() << std::endl; - outFile.close(); + FILE *outFile = File::OpenCFile(file, "wb"); + if (outFile != nullptr) { + fprintf(outFile, "_S %s\n", g_paramSFO.GetDiscID().c_str()); + fclose(outFile); + } g_Config.bReloadCheats = true; @@ -1880,10 +1881,20 @@ void retro_cheat_set(unsigned index, bool enabled, const char *code) { // Read cheats file std::vector cheats; - std::ifstream cheat_content(file.c_str()); - std::stringstream buffer; - buffer << cheat_content.rdbuf(); - std::string existing_cheats=ReplaceAll(buffer.str(), std::string("\n_C"), std::string("|")); + std::string cheat_content; + FILE *inFile = File::OpenCFile(file, "rb"); + if (inFile != nullptr) { + std::array buffer; + for (;;) { + size_t n = fread(buffer.data(), 1, buffer.size(), inFile); + cheat_content.append((const char *)buffer.data(), n); + if (n < buffer.size()) { + break; + } + } + fclose(inFile); + } + std::string existing_cheats=ReplaceAll(cheat_content, std::string("\n_C"), std::string("|")); SplitString(existing_cheats, '|', cheats); // Generate Cheat String @@ -1915,13 +1926,14 @@ void retro_cheat_set(unsigned index, bool enabled, const char *code) { } // Output cheats to cheat file - std::ofstream outFile; - outFile.open(file.c_str()); - outFile << "_S " << g_paramSFO.GetDiscID() << std::endl; - for (int i=1; i < cheats.size(); i++) { - outFile << "_C" << cheats[i] << std::endl; + FILE *outFile = File::OpenCFile(file, "wb"); + if (outFile != nullptr) { + fprintf(outFile, "_S %s\n", g_paramSFO.GetDiscID().c_str()); + for (const std::string &cheat : cheats) { + fprintf(outFile, "_C%s\n", cheat.c_str()); + } + fclose(outFile); } - outFile.close(); g_Config.bReloadCheats = true; diff --git a/libretro/libretro.h b/libretro/libretro.h deleted file mode 100644 index 5068ca107a..0000000000 --- a/libretro/libretro.h +++ /dev/null @@ -1,3968 +0,0 @@ -/* Copyright (C) 2010-2020 The RetroArch team - * - * --------------------------------------------------------------------------------------- - * The following license statement only applies to this libretro API header (libretro.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_H__ -#define LIBRETRO_H__ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __cplusplus -#if defined(_MSC_VER) && _MSC_VER < 1800 && !defined(SN_TARGET_PS3) -/* Hack applied for MSVC when compiling in C89 mode - * as it isn't C99-compliant. */ -#define bool unsigned char -#define true 1 -#define false 0 -#else -#include -#endif -#endif - -#ifndef RETRO_CALLCONV -# if defined(__GNUC__) && defined(__i386__) && !defined(__x86_64__) -# define RETRO_CALLCONV __attribute__((cdecl)) -# elif defined(_MSC_VER) && defined(_M_X86) && !defined(_M_X64) -# define RETRO_CALLCONV __cdecl -# else -# define RETRO_CALLCONV /* all other platforms only have one calling convention each */ -# endif -#endif - -#ifndef RETRO_API -# if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) -# ifdef RETRO_IMPORT_SYMBOLS -# ifdef __GNUC__ -# define RETRO_API RETRO_CALLCONV __attribute__((__dllimport__)) -# else -# define RETRO_API RETRO_CALLCONV __declspec(dllimport) -# endif -# else -# ifdef __GNUC__ -# define RETRO_API RETRO_CALLCONV __attribute__((__dllexport__)) -# else -# define RETRO_API RETRO_CALLCONV __declspec(dllexport) -# endif -# endif -# else -# if defined(__GNUC__) && __GNUC__ >= 4 -# define RETRO_API RETRO_CALLCONV __attribute__((__visibility__("default"))) -# else -# define RETRO_API RETRO_CALLCONV -# endif -# endif -#endif - -/* Used for checking API/ABI mismatches that can break libretro - * implementations. - * It is not incremented for compatible changes to the API. - */ -#define RETRO_API_VERSION 1 - -/* - * Libretro's fundamental device abstractions. - * - * Libretro's input system consists of some standardized device types, - * such as a joypad (with/without analog), mouse, keyboard, lightgun - * and a pointer. - * - * The functionality of these devices are fixed, and individual cores - * map their own concept of a controller to libretro's abstractions. - * This makes it possible for frontends to map the abstract types to a - * real input device, and not having to worry about binding input - * correctly to arbitrary controller layouts. - */ - -#define RETRO_DEVICE_TYPE_SHIFT 8 -#define RETRO_DEVICE_MASK ((1 << RETRO_DEVICE_TYPE_SHIFT) - 1) -#define RETRO_DEVICE_SUBCLASS(base, id) (((id + 1) << RETRO_DEVICE_TYPE_SHIFT) | base) - -/* Input disabled. */ -#define RETRO_DEVICE_NONE 0 - -/* The JOYPAD is called RetroPad. It is essentially a Super Nintendo - * controller, but with additional L2/R2/L3/R3 buttons, similar to a - * PS1 DualShock. */ -#define RETRO_DEVICE_JOYPAD 1 - -/* The mouse is a simple mouse, similar to Super Nintendo's mouse. - * X and Y coordinates are reported relatively to last poll (poll callback). - * It is up to the libretro implementation to keep track of where the mouse - * pointer is supposed to be on the screen. - * The frontend must make sure not to interfere with its own hardware - * mouse pointer. - */ -#define RETRO_DEVICE_MOUSE 2 - -/* KEYBOARD device lets one poll for raw key pressed. - * It is poll based, so input callback will return with the current - * pressed state. - * For event/text based keyboard input, see - * RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. - */ -#define RETRO_DEVICE_KEYBOARD 3 - -/* LIGHTGUN device is similar to Guncon-2 for PlayStation 2. - * It reports X/Y coordinates in screen space (similar to the pointer) - * in the range [-0x8000, 0x7fff] in both axes, with zero being center and - * -0x8000 being out of bounds. - * As well as reporting on/off screen state. It features a trigger, - * start/select buttons, auxiliary action buttons and a - * directional pad. A forced off-screen shot can be requested for - * auto-reloading function in some games. - */ -#define RETRO_DEVICE_LIGHTGUN 4 - -/* The ANALOG device is an extension to JOYPAD (RetroPad). - * Similar to DualShock2 it adds two analog sticks and all buttons can - * be analog. This is treated as a separate device type as it returns - * axis values in the full analog range of [-0x7fff, 0x7fff], - * although some devices may return -0x8000. - * Positive X axis is right. Positive Y axis is down. - * Buttons are returned in the range [0, 0x7fff]. - * Only use ANALOG type when polling for analog values. - */ -#define RETRO_DEVICE_ANALOG 5 - -/* Abstracts the concept of a pointing mechanism, e.g. touch. - * This allows libretro to query in absolute coordinates where on the - * screen a mouse (or something similar) is being placed. - * For a touch centric device, coordinates reported are the coordinates - * of the press. - * - * Coordinates in X and Y are reported as: - * [-0x7fff, 0x7fff]: -0x7fff corresponds to the far left/top of the screen, - * and 0x7fff corresponds to the far right/bottom of the screen. - * The "screen" is here defined as area that is passed to the frontend and - * later displayed on the monitor. - * - * The frontend is free to scale/resize this screen as it sees fit, however, - * (X, Y) = (-0x7fff, -0x7fff) will correspond to the top-left pixel of the - * game image, etc. - * - * To check if the pointer coordinates are valid (e.g. a touch display - * actually being touched), PRESSED returns 1 or 0. - * - * If using a mouse on a desktop, PRESSED will usually correspond to the - * left mouse button, but this is a frontend decision. - * PRESSED will only return 1 if the pointer is inside the game screen. - * - * For multi-touch, the index variable can be used to successively query - * more presses. - * If index = 0 returns true for _PRESSED, coordinates can be extracted - * with _X, _Y for index = 0. One can then query _PRESSED, _X, _Y with - * index = 1, and so on. - * Eventually _PRESSED will return false for an index. No further presses - * are registered at this point. */ -#define RETRO_DEVICE_POINTER 6 - -/* Buttons for the RetroPad (JOYPAD). - * The placement of these is equivalent to placements on the - * Super Nintendo controller. - * L2/R2/L3/R3 buttons correspond to the PS1 DualShock. - * Also used as id values for RETRO_DEVICE_INDEX_ANALOG_BUTTON */ -#define RETRO_DEVICE_ID_JOYPAD_B 0 -#define RETRO_DEVICE_ID_JOYPAD_Y 1 -#define RETRO_DEVICE_ID_JOYPAD_SELECT 2 -#define RETRO_DEVICE_ID_JOYPAD_START 3 -#define RETRO_DEVICE_ID_JOYPAD_UP 4 -#define RETRO_DEVICE_ID_JOYPAD_DOWN 5 -#define RETRO_DEVICE_ID_JOYPAD_LEFT 6 -#define RETRO_DEVICE_ID_JOYPAD_RIGHT 7 -#define RETRO_DEVICE_ID_JOYPAD_A 8 -#define RETRO_DEVICE_ID_JOYPAD_X 9 -#define RETRO_DEVICE_ID_JOYPAD_L 10 -#define RETRO_DEVICE_ID_JOYPAD_R 11 -#define RETRO_DEVICE_ID_JOYPAD_L2 12 -#define RETRO_DEVICE_ID_JOYPAD_R2 13 -#define RETRO_DEVICE_ID_JOYPAD_L3 14 -#define RETRO_DEVICE_ID_JOYPAD_R3 15 - -#define RETRO_DEVICE_ID_JOYPAD_MASK 256 - -/* Index / Id values for ANALOG device. */ -#define RETRO_DEVICE_INDEX_ANALOG_LEFT 0 -#define RETRO_DEVICE_INDEX_ANALOG_RIGHT 1 -#define RETRO_DEVICE_INDEX_ANALOG_BUTTON 2 -#define RETRO_DEVICE_ID_ANALOG_X 0 -#define RETRO_DEVICE_ID_ANALOG_Y 1 - -/* Id values for MOUSE. */ -#define RETRO_DEVICE_ID_MOUSE_X 0 -#define RETRO_DEVICE_ID_MOUSE_Y 1 -#define RETRO_DEVICE_ID_MOUSE_LEFT 2 -#define RETRO_DEVICE_ID_MOUSE_RIGHT 3 -#define RETRO_DEVICE_ID_MOUSE_WHEELUP 4 -#define RETRO_DEVICE_ID_MOUSE_WHEELDOWN 5 -#define RETRO_DEVICE_ID_MOUSE_MIDDLE 6 -#define RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP 7 -#define RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN 8 -#define RETRO_DEVICE_ID_MOUSE_BUTTON_4 9 -#define RETRO_DEVICE_ID_MOUSE_BUTTON_5 10 - -/* Id values for LIGHTGUN. */ -#define RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X 13 /*Absolute Position*/ -#define RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y 14 /*Absolute*/ -#define RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN 15 /*Status Check*/ -#define RETRO_DEVICE_ID_LIGHTGUN_TRIGGER 2 -#define RETRO_DEVICE_ID_LIGHTGUN_RELOAD 16 /*Forced off-screen shot*/ -#define RETRO_DEVICE_ID_LIGHTGUN_AUX_A 3 -#define RETRO_DEVICE_ID_LIGHTGUN_AUX_B 4 -#define RETRO_DEVICE_ID_LIGHTGUN_START 6 -#define RETRO_DEVICE_ID_LIGHTGUN_SELECT 7 -#define RETRO_DEVICE_ID_LIGHTGUN_AUX_C 8 -#define RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP 9 -#define RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN 10 -#define RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT 11 -#define RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT 12 -/* deprecated */ -#define RETRO_DEVICE_ID_LIGHTGUN_X 0 /*Relative Position*/ -#define RETRO_DEVICE_ID_LIGHTGUN_Y 1 /*Relative*/ -#define RETRO_DEVICE_ID_LIGHTGUN_CURSOR 3 /*Use Aux:A*/ -#define RETRO_DEVICE_ID_LIGHTGUN_TURBO 4 /*Use Aux:B*/ -#define RETRO_DEVICE_ID_LIGHTGUN_PAUSE 5 /*Use Start*/ - -/* Id values for POINTER. */ -#define RETRO_DEVICE_ID_POINTER_X 0 -#define RETRO_DEVICE_ID_POINTER_Y 1 -#define RETRO_DEVICE_ID_POINTER_PRESSED 2 -#define RETRO_DEVICE_ID_POINTER_COUNT 3 - -/* Returned from retro_get_region(). */ -#define RETRO_REGION_NTSC 0 -#define RETRO_REGION_PAL 1 - -/* Id values for LANGUAGE */ -enum retro_language -{ - RETRO_LANGUAGE_ENGLISH = 0, - RETRO_LANGUAGE_JAPANESE = 1, - RETRO_LANGUAGE_FRENCH = 2, - RETRO_LANGUAGE_SPANISH = 3, - RETRO_LANGUAGE_GERMAN = 4, - RETRO_LANGUAGE_ITALIAN = 5, - RETRO_LANGUAGE_DUTCH = 6, - RETRO_LANGUAGE_PORTUGUESE_BRAZIL = 7, - RETRO_LANGUAGE_PORTUGUESE_PORTUGAL = 8, - RETRO_LANGUAGE_RUSSIAN = 9, - RETRO_LANGUAGE_KOREAN = 10, - RETRO_LANGUAGE_CHINESE_TRADITIONAL = 11, - RETRO_LANGUAGE_CHINESE_SIMPLIFIED = 12, - RETRO_LANGUAGE_ESPERANTO = 13, - RETRO_LANGUAGE_POLISH = 14, - RETRO_LANGUAGE_VIETNAMESE = 15, - RETRO_LANGUAGE_ARABIC = 16, - RETRO_LANGUAGE_GREEK = 17, - RETRO_LANGUAGE_TURKISH = 18, - RETRO_LANGUAGE_SLOVAK = 19, - RETRO_LANGUAGE_PERSIAN = 20, - RETRO_LANGUAGE_HEBREW = 21, - RETRO_LANGUAGE_ASTURIAN = 22, - RETRO_LANGUAGE_FINNISH = 23, - RETRO_LANGUAGE_INDONESIAN = 24, - RETRO_LANGUAGE_SWEDISH = 25, - RETRO_LANGUAGE_UKRAINIAN = 26, - RETRO_LANGUAGE_CZECH = 27, - RETRO_LANGUAGE_CATALAN_VALENCIA = 28, - RETRO_LANGUAGE_CATALAN = 29, - RETRO_LANGUAGE_LAST, - - /* Ensure sizeof(enum) == sizeof(int) */ - RETRO_LANGUAGE_DUMMY = INT_MAX -}; - -/* Passed to retro_get_memory_data/size(). - * If the memory type doesn't apply to the - * implementation NULL/0 can be returned. - */ -#define RETRO_MEMORY_MASK 0xff - -/* Regular save RAM. This RAM is usually found on a game cartridge, - * backed up by a battery. - * If save game data is too complex for a single memory buffer, - * the SAVE_DIRECTORY (preferably) or SYSTEM_DIRECTORY environment - * callback can be used. */ -#define RETRO_MEMORY_SAVE_RAM 0 - -/* Some games have a built-in clock to keep track of time. - * This memory is usually just a couple of bytes to keep track of time. - */ -#define RETRO_MEMORY_RTC 1 - -/* System ram lets a frontend peek into a game systems main RAM. */ -#define RETRO_MEMORY_SYSTEM_RAM 2 - -/* Video ram lets a frontend peek into a game systems video RAM (VRAM). */ -#define RETRO_MEMORY_VIDEO_RAM 3 - -/* Keysyms used for ID in input state callback when polling RETRO_KEYBOARD. */ -enum retro_key -{ - RETROK_UNKNOWN = 0, - RETROK_FIRST = 0, - RETROK_BACKSPACE = 8, - RETROK_TAB = 9, - RETROK_CLEAR = 12, - RETROK_RETURN = 13, - RETROK_PAUSE = 19, - RETROK_ESCAPE = 27, - RETROK_SPACE = 32, - RETROK_EXCLAIM = 33, - RETROK_QUOTEDBL = 34, - RETROK_HASH = 35, - RETROK_DOLLAR = 36, - RETROK_AMPERSAND = 38, - RETROK_QUOTE = 39, - RETROK_LEFTPAREN = 40, - RETROK_RIGHTPAREN = 41, - RETROK_ASTERISK = 42, - RETROK_PLUS = 43, - RETROK_COMMA = 44, - RETROK_MINUS = 45, - RETROK_PERIOD = 46, - RETROK_SLASH = 47, - RETROK_0 = 48, - RETROK_1 = 49, - RETROK_2 = 50, - RETROK_3 = 51, - RETROK_4 = 52, - RETROK_5 = 53, - RETROK_6 = 54, - RETROK_7 = 55, - RETROK_8 = 56, - RETROK_9 = 57, - RETROK_COLON = 58, - RETROK_SEMICOLON = 59, - RETROK_LESS = 60, - RETROK_EQUALS = 61, - RETROK_GREATER = 62, - RETROK_QUESTION = 63, - RETROK_AT = 64, - RETROK_LEFTBRACKET = 91, - RETROK_BACKSLASH = 92, - RETROK_RIGHTBRACKET = 93, - RETROK_CARET = 94, - RETROK_UNDERSCORE = 95, - RETROK_BACKQUOTE = 96, - RETROK_a = 97, - RETROK_b = 98, - RETROK_c = 99, - RETROK_d = 100, - RETROK_e = 101, - RETROK_f = 102, - RETROK_g = 103, - RETROK_h = 104, - RETROK_i = 105, - RETROK_j = 106, - RETROK_k = 107, - RETROK_l = 108, - RETROK_m = 109, - RETROK_n = 110, - RETROK_o = 111, - RETROK_p = 112, - RETROK_q = 113, - RETROK_r = 114, - RETROK_s = 115, - RETROK_t = 116, - RETROK_u = 117, - RETROK_v = 118, - RETROK_w = 119, - RETROK_x = 120, - RETROK_y = 121, - RETROK_z = 122, - RETROK_LEFTBRACE = 123, - RETROK_BAR = 124, - RETROK_RIGHTBRACE = 125, - RETROK_TILDE = 126, - RETROK_DELETE = 127, - - RETROK_KP0 = 256, - RETROK_KP1 = 257, - RETROK_KP2 = 258, - RETROK_KP3 = 259, - RETROK_KP4 = 260, - RETROK_KP5 = 261, - RETROK_KP6 = 262, - RETROK_KP7 = 263, - RETROK_KP8 = 264, - RETROK_KP9 = 265, - RETROK_KP_PERIOD = 266, - RETROK_KP_DIVIDE = 267, - RETROK_KP_MULTIPLY = 268, - RETROK_KP_MINUS = 269, - RETROK_KP_PLUS = 270, - RETROK_KP_ENTER = 271, - RETROK_KP_EQUALS = 272, - - RETROK_UP = 273, - RETROK_DOWN = 274, - RETROK_RIGHT = 275, - RETROK_LEFT = 276, - RETROK_INSERT = 277, - RETROK_HOME = 278, - RETROK_END = 279, - RETROK_PAGEUP = 280, - RETROK_PAGEDOWN = 281, - - RETROK_F1 = 282, - RETROK_F2 = 283, - RETROK_F3 = 284, - RETROK_F4 = 285, - RETROK_F5 = 286, - RETROK_F6 = 287, - RETROK_F7 = 288, - RETROK_F8 = 289, - RETROK_F9 = 290, - RETROK_F10 = 291, - RETROK_F11 = 292, - RETROK_F12 = 293, - RETROK_F13 = 294, - RETROK_F14 = 295, - RETROK_F15 = 296, - - RETROK_NUMLOCK = 300, - RETROK_CAPSLOCK = 301, - RETROK_SCROLLOCK = 302, - RETROK_RSHIFT = 303, - RETROK_LSHIFT = 304, - RETROK_RCTRL = 305, - RETROK_LCTRL = 306, - RETROK_RALT = 307, - RETROK_LALT = 308, - RETROK_RMETA = 309, - RETROK_LMETA = 310, - RETROK_LSUPER = 311, - RETROK_RSUPER = 312, - RETROK_MODE = 313, - RETROK_COMPOSE = 314, - - RETROK_HELP = 315, - RETROK_PRINT = 316, - RETROK_SYSREQ = 317, - RETROK_BREAK = 318, - RETROK_MENU = 319, - RETROK_POWER = 320, - RETROK_EURO = 321, - RETROK_UNDO = 322, - RETROK_OEM_102 = 323, - - RETROK_LAST, - - RETROK_DUMMY = INT_MAX /* Ensure sizeof(enum) == sizeof(int) */ -}; - -enum retro_mod -{ - RETROKMOD_NONE = 0x0000, - - RETROKMOD_SHIFT = 0x01, - RETROKMOD_CTRL = 0x02, - RETROKMOD_ALT = 0x04, - RETROKMOD_META = 0x08, - - RETROKMOD_NUMLOCK = 0x10, - RETROKMOD_CAPSLOCK = 0x20, - RETROKMOD_SCROLLOCK = 0x40, - - RETROKMOD_DUMMY = INT_MAX /* Ensure sizeof(enum) == sizeof(int) */ -}; - -/* If set, this call is not part of the public libretro API yet. It can - * change or be removed at any time. */ -#define RETRO_ENVIRONMENT_EXPERIMENTAL 0x10000 -/* Environment callback to be used internally in frontend. */ -#define RETRO_ENVIRONMENT_PRIVATE 0x20000 - -/* Environment commands. */ -#define RETRO_ENVIRONMENT_SET_ROTATION 1 /* const unsigned * -- - * Sets screen rotation of graphics. - * Valid values are 0, 1, 2, 3, which rotates screen by 0, 90, 180, - * 270 degrees counter-clockwise respectively. - */ -#define RETRO_ENVIRONMENT_GET_OVERSCAN 2 /* bool * -- - * NOTE: As of 2019 this callback is considered deprecated in favor of - * using core options to manage overscan in a more nuanced, core-specific way. - * - * Boolean value whether or not the implementation should use overscan, - * or crop away overscan. - */ -#define RETRO_ENVIRONMENT_GET_CAN_DUPE 3 /* bool * -- - * Boolean value whether or not frontend supports frame duping, - * passing NULL to video frame callback. - */ - - /* Environ 4, 5 are no longer supported (GET_VARIABLE / SET_VARIABLES), - * and reserved to avoid possible ABI clash. - */ - -#define RETRO_ENVIRONMENT_SET_MESSAGE 6 /* const struct retro_message * -- - * Sets a message to be displayed in implementation-specific manner - * for a certain amount of 'frames'. - * Should not be used for trivial messages, which should simply be - * logged via RETRO_ENVIRONMENT_GET_LOG_INTERFACE (or as a - * fallback, stderr). - */ -#define RETRO_ENVIRONMENT_SHUTDOWN 7 /* N/A (NULL) -- - * Requests the frontend to shutdown. - * Should only be used if game has a specific - * way to shutdown the game from a menu item or similar. - */ -#define RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL 8 - /* const unsigned * -- - * Gives a hint to the frontend how demanding this implementation - * is on a system. E.g. reporting a level of 2 means - * this implementation should run decently on all frontends - * of level 2 and up. - * - * It can be used by the frontend to potentially warn - * about too demanding implementations. - * - * The levels are "floating". - * - * This function can be called on a per-game basis, - * as certain games an implementation can play might be - * particularly demanding. - * If called, it should be called in retro_load_game(). - */ -#define RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY 9 - /* const char ** -- - * Returns the "system" directory of the frontend. - * This directory can be used to store system specific - * content such as BIOSes, configuration data, etc. - * The returned value can be NULL. - * If so, no such directory is defined, - * and it's up to the implementation to find a suitable directory. - * - * NOTE: Some cores used this folder also for "save" data such as - * memory cards, etc, for lack of a better place to put it. - * This is now discouraged, and if possible, cores should try to - * use the new GET_SAVE_DIRECTORY. - */ -#define RETRO_ENVIRONMENT_SET_PIXEL_FORMAT 10 - /* const enum retro_pixel_format * -- - * Sets the internal pixel format used by the implementation. - * The default pixel format is RETRO_PIXEL_FORMAT_0RGB1555. - * This pixel format however, is deprecated (see enum retro_pixel_format). - * If the call returns false, the frontend does not support this pixel - * format. - * - * This function should be called inside retro_load_game() or - * retro_get_system_av_info(). - */ -#define RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS 11 - /* const struct retro_input_descriptor * -- - * Sets an array of retro_input_descriptors. - * It is up to the frontend to present this in a usable way. - * The array is terminated by retro_input_descriptor::description - * being set to NULL. - * This function can be called at any time, but it is recommended - * to call it as early as possible. - */ -#define RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK 12 - /* const struct retro_keyboard_callback * -- - * Sets a callback function used to notify core about keyboard events. - */ -#define RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE 13 - /* const struct retro_disk_control_callback * -- - * Sets an interface which frontend can use to eject and insert - * disk images. - * This is used for games which consist of multiple images and - * must be manually swapped out by the user (e.g. PSX). - */ -#define RETRO_ENVIRONMENT_SET_HW_RENDER 14 - /* struct retro_hw_render_callback * -- - * Sets an interface to let a libretro core render with - * hardware acceleration. - * Should be called in retro_load_game(). - * If successful, libretro cores will be able to render to a - * frontend-provided framebuffer. - * The size of this framebuffer will be at least as large as - * max_width/max_height provided in get_av_info(). - * If HW rendering is used, pass only RETRO_HW_FRAME_BUFFER_VALID or - * NULL to retro_video_refresh_t. - */ -#define RETRO_ENVIRONMENT_GET_VARIABLE 15 - /* struct retro_variable * -- - * Interface to acquire user-defined information from environment - * that cannot feasibly be supported in a multi-system way. - * 'key' should be set to a key which has already been set by - * SET_VARIABLES. - * 'data' will be set to a value or NULL. - */ -#define RETRO_ENVIRONMENT_SET_VARIABLES 16 - /* const struct retro_variable * -- - * Allows an implementation to signal the environment - * which variables it might want to check for later using - * GET_VARIABLE. - * This allows the frontend to present these variables to - * a user dynamically. - * This should be called the first time as early as - * possible (ideally in retro_set_environment). - * Afterward it may be called again for the core to communicate - * updated options to the frontend, but the number of core - * options must not change from the number in the initial call. - * - * 'data' points to an array of retro_variable structs - * terminated by a { NULL, NULL } element. - * retro_variable::key should be namespaced to not collide - * with other implementations' keys. E.g. A core called - * 'foo' should use keys named as 'foo_option'. - * retro_variable::value should contain a human readable - * description of the key as well as a '|' delimited list - * of expected values. - * - * The number of possible options should be very limited, - * i.e. it should be feasible to cycle through options - * without a keyboard. - * - * First entry should be treated as a default. - * - * Example entry: - * { "foo_option", "Speed hack coprocessor X; false|true" } - * - * Text before first ';' is description. This ';' must be - * followed by a space, and followed by a list of possible - * values split up with '|'. - * - * Only strings are operated on. The possible values will - * generally be displayed and stored as-is by the frontend. - */ -#define RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE 17 - /* bool * -- - * Result is set to true if some variables are updated by - * frontend since last call to RETRO_ENVIRONMENT_GET_VARIABLE. - * Variables should be queried with GET_VARIABLE. - */ -#define RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME 18 - /* const bool * -- - * If true, the libretro implementation supports calls to - * retro_load_game() with NULL as argument. - * Used by cores which can run without particular game data. - * This should be called within retro_set_environment() only. - */ -#define RETRO_ENVIRONMENT_GET_LIBRETRO_PATH 19 - /* const char ** -- - * Retrieves the absolute path from where this libretro - * implementation was loaded. - * NULL is returned if the libretro was loaded statically - * (i.e. linked statically to frontend), or if the path cannot be - * determined. - * Mostly useful in cooperation with SET_SUPPORT_NO_GAME as assets can - * be loaded without ugly hacks. - */ - - /* Environment 20 was an obsolete version of SET_AUDIO_CALLBACK. - * It was not used by any known core at the time, - * and was removed from the API. */ -#define RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK 21 - /* const struct retro_frame_time_callback * -- - * Lets the core know how much time has passed since last - * invocation of retro_run(). - * The frontend can tamper with the timing to fake fast-forward, - * slow-motion, frame stepping, etc. - * In this case the delta time will use the reference value - * in frame_time_callback.. - */ -#define RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK 22 - /* const struct retro_audio_callback * -- - * Sets an interface which is used to notify a libretro core about audio - * being available for writing. - * The callback can be called from any thread, so a core using this must - * have a thread safe audio implementation. - * It is intended for games where audio and video are completely - * asynchronous and audio can be generated on the fly. - * This interface is not recommended for use with emulators which have - * highly synchronous audio. - * - * The callback only notifies about writability; the libretro core still - * has to call the normal audio callbacks - * to write audio. The audio callbacks must be called from within the - * notification callback. - * The amount of audio data to write is up to the implementation. - * Generally, the audio callback will be called continously in a loop. - * - * Due to thread safety guarantees and lack of sync between audio and - * video, a frontend can selectively disallow this interface based on - * internal configuration. A core using this interface must also - * implement the "normal" audio interface. - * - * A libretro core using SET_AUDIO_CALLBACK should also make use of - * SET_FRAME_TIME_CALLBACK. - */ -#define RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE 23 - /* struct retro_rumble_interface * -- - * Gets an interface which is used by a libretro core to set - * state of rumble motors in controllers. - * A strong and weak motor is supported, and they can be - * controlled indepedently. - * Should be called from either retro_init() or retro_load_game(). - * Should not be called from retro_set_environment(). - * Returns false if rumble functionality is unavailable. - */ -#define RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES 24 - /* uint64_t * -- - * Gets a bitmask telling which device type are expected to be - * handled properly in a call to retro_input_state_t. - * Devices which are not handled or recognized always return - * 0 in retro_input_state_t. - * Example bitmask: caps = (1 << RETRO_DEVICE_JOYPAD) | (1 << RETRO_DEVICE_ANALOG). - * Should only be called in retro_run(). - */ -#define RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE (25 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* struct retro_sensor_interface * -- - * Gets access to the sensor interface. - * The purpose of this interface is to allow - * setting state related to sensors such as polling rate, - * enabling/disable it entirely, etc. - * Reading sensor state is done via the normal - * input_state_callback API. - */ -#define RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE (26 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* struct retro_camera_callback * -- - * Gets an interface to a video camera driver. - * A libretro core can use this interface to get access to a - * video camera. - * New video frames are delivered in a callback in same - * thread as retro_run(). - * - * GET_CAMERA_INTERFACE should be called in retro_load_game(). - * - * Depending on the camera implementation used, camera frames - * will be delivered as a raw framebuffer, - * or as an OpenGL texture directly. - * - * The core has to tell the frontend here which types of - * buffers can be handled properly. - * An OpenGL texture can only be handled when using a - * libretro GL core (SET_HW_RENDER). - * It is recommended to use a libretro GL core when - * using camera interface. - * - * The camera is not started automatically. The retrieved start/stop - * functions must be used to explicitly - * start and stop the camera driver. - */ -#define RETRO_ENVIRONMENT_GET_LOG_INTERFACE 27 - /* struct retro_log_callback * -- - * Gets an interface for logging. This is useful for - * logging in a cross-platform way - * as certain platforms cannot use stderr for logging. - * It also allows the frontend to - * show logging information in a more suitable way. - * If this interface is not used, libretro cores should - * log to stderr as desired. - */ -#define RETRO_ENVIRONMENT_GET_PERF_INTERFACE 28 - /* struct retro_perf_callback * -- - * Gets an interface for performance counters. This is useful - * for performance logging in a cross-platform way and for detecting - * architecture-specific features, such as SIMD support. - */ -#define RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE 29 - /* struct retro_location_callback * -- - * Gets access to the location interface. - * The purpose of this interface is to be able to retrieve - * location-based information from the host device, - * such as current latitude / longitude. - */ -#define RETRO_ENVIRONMENT_GET_CONTENT_DIRECTORY 30 /* Old name, kept for compatibility. */ -#define RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY 30 - /* const char ** -- - * Returns the "core assets" directory of the frontend. - * This directory can be used to store specific assets that the - * core relies upon, such as art assets, - * input data, etc etc. - * The returned value can be NULL. - * If so, no such directory is defined, - * and it's up to the implementation to find a suitable directory. - */ -#define RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY 31 - /* const char ** -- - * Returns the "save" directory of the frontend, unless there is no - * save directory available. The save directory should be used to - * store SRAM, memory cards, high scores, etc, if the libretro core - * cannot use the regular memory interface (retro_get_memory_data()). - * - * If the frontend cannot designate a save directory, it will return - * NULL to indicate that the core should attempt to operate without a - * save directory set. - * - * NOTE: early libretro cores used the system directory for save - * files. Cores that need to be backwards-compatible can still check - * GET_SYSTEM_DIRECTORY. - */ -#define RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO 32 - /* const struct retro_system_av_info * -- - * Sets a new av_info structure. This can only be called from - * within retro_run(). - * This should *only* be used if the core is completely altering the - * internal resolutions, aspect ratios, timings, sampling rate, etc. - * Calling this can require a full reinitialization of video/audio - * drivers in the frontend, - * - * so it is important to call it very sparingly, and usually only with - * the users explicit consent. - * An eventual driver reinitialize will happen so that video and - * audio callbacks - * happening after this call within the same retro_run() call will - * target the newly initialized driver. - * - * This callback makes it possible to support configurable resolutions - * in games, which can be useful to - * avoid setting the "worst case" in max_width/max_height. - * - * ***HIGHLY RECOMMENDED*** Do not call this callback every time - * resolution changes in an emulator core if it's - * expected to be a temporary change, for the reasons of possible - * driver reinitialization. - * This call is not a free pass for not trying to provide - * correct values in retro_get_system_av_info(). If you need to change - * things like aspect ratio or nominal width/height, - * use RETRO_ENVIRONMENT_SET_GEOMETRY, which is a softer variant - * of SET_SYSTEM_AV_INFO. - * - * If this returns false, the frontend does not acknowledge a - * changed av_info struct. - */ -#define RETRO_ENVIRONMENT_SET_PROC_ADDRESS_CALLBACK 33 - /* const struct retro_get_proc_address_interface * -- - * Allows a libretro core to announce support for the - * get_proc_address() interface. - * This interface allows for a standard way to extend libretro where - * use of environment calls are too indirect, - * e.g. for cases where the frontend wants to call directly into the core. - * - * If a core wants to expose this interface, SET_PROC_ADDRESS_CALLBACK - * **MUST** be called from within retro_set_environment(). - */ -#define RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO 34 - /* const struct retro_subsystem_info * -- - * This environment call introduces the concept of libretro "subsystems". - * A subsystem is a variant of a libretro core which supports - * different kinds of games. - * The purpose of this is to support e.g. emulators which might - * have special needs, e.g. Super Nintendo's Super GameBoy, Sufami Turbo. - * It can also be used to pick among subsystems in an explicit way - * if the libretro implementation is a multi-system emulator itself. - * - * Loading a game via a subsystem is done with retro_load_game_special(), - * and this environment call allows a libretro core to expose which - * subsystems are supported for use with retro_load_game_special(). - * A core passes an array of retro_game_special_info which is terminated - * with a zeroed out retro_game_special_info struct. - * - * If a core wants to use this functionality, SET_SUBSYSTEM_INFO - * **MUST** be called from within retro_set_environment(). - */ -#define RETRO_ENVIRONMENT_SET_CONTROLLER_INFO 35 - /* const struct retro_controller_info * -- - * This environment call lets a libretro core tell the frontend - * which controller subclasses are recognized in calls to - * retro_set_controller_port_device(). - * - * Some emulators such as Super Nintendo support multiple lightgun - * types which must be specifically selected from. It is therefore - * sometimes necessary for a frontend to be able to tell the core - * about a special kind of input device which is not specifcally - * provided by the Libretro API. - * - * In order for a frontend to understand the workings of those devices, - * they must be defined as a specialized subclass of the generic device - * types already defined in the libretro API. - * - * The core must pass an array of const struct retro_controller_info which - * is terminated with a blanked out struct. Each element of the - * retro_controller_info struct corresponds to the ascending port index - * that is passed to retro_set_controller_port_device() when that function - * is called to indicate to the core that the frontend has changed the - * active device subclass. SEE ALSO: retro_set_controller_port_device() - * - * The ascending input port indexes provided by the core in the struct - * are generally presented by frontends as ascending User # or Player #, - * such as Player 1, Player 2, Player 3, etc. Which device subclasses are - * supported can vary per input port. - * - * The first inner element of each entry in the retro_controller_info array - * is a retro_controller_description struct that specifies the names and - * codes of all device subclasses that are available for the corresponding - * User or Player, beginning with the generic Libretro device that the - * subclasses are derived from. The second inner element of each entry is the - * total number of subclasses that are listed in the retro_controller_description. - * - * NOTE: Even if special device types are set in the libretro core, - * libretro should only poll input based on the base input device types. - */ -#define RETRO_ENVIRONMENT_SET_MEMORY_MAPS (36 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* const struct retro_memory_map * -- - * This environment call lets a libretro core tell the frontend - * about the memory maps this core emulates. - * This can be used to implement, for example, cheats in a core-agnostic way. - * - * Should only be used by emulators; it doesn't make much sense for - * anything else. - * It is recommended to expose all relevant pointers through - * retro_get_memory_* as well. - * - * Can be called from retro_init and retro_load_game. - */ -#define RETRO_ENVIRONMENT_SET_GEOMETRY 37 - /* const struct retro_game_geometry * -- - * This environment call is similar to SET_SYSTEM_AV_INFO for changing - * video parameters, but provides a guarantee that drivers will not be - * reinitialized. - * This can only be called from within retro_run(). - * - * The purpose of this call is to allow a core to alter nominal - * width/heights as well as aspect ratios on-the-fly, which can be - * useful for some emulators to change in run-time. - * - * max_width/max_height arguments are ignored and cannot be changed - * with this call as this could potentially require a reinitialization or a - * non-constant time operation. - * If max_width/max_height are to be changed, SET_SYSTEM_AV_INFO is required. - * - * A frontend must guarantee that this environment call completes in - * constant time. - */ -#define RETRO_ENVIRONMENT_GET_USERNAME 38 - /* const char ** - * Returns the specified username of the frontend, if specified by the user. - * This username can be used as a nickname for a core that has online facilities - * or any other mode where personalization of the user is desirable. - * The returned value can be NULL. - * If this environ callback is used by a core that requires a valid username, - * a default username should be specified by the core. - */ -#define RETRO_ENVIRONMENT_GET_LANGUAGE 39 - /* unsigned * -- - * Returns the specified language of the frontend, if specified by the user. - * It can be used by the core for localization purposes. - */ -#define RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER (40 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* struct retro_framebuffer * -- - * Returns a preallocated framebuffer which the core can use for rendering - * the frame into when not using SET_HW_RENDER. - * The framebuffer returned from this call must not be used - * after the current call to retro_run() returns. - * - * The goal of this call is to allow zero-copy behavior where a core - * can render directly into video memory, avoiding extra bandwidth cost by copying - * memory from core to video memory. - * - * If this call succeeds and the core renders into it, - * the framebuffer pointer and pitch can be passed to retro_video_refresh_t. - * If the buffer from GET_CURRENT_SOFTWARE_FRAMEBUFFER is to be used, - * the core must pass the exact - * same pointer as returned by GET_CURRENT_SOFTWARE_FRAMEBUFFER; - * i.e. passing a pointer which is offset from the - * buffer is undefined. The width, height and pitch parameters - * must also match exactly to the values obtained from GET_CURRENT_SOFTWARE_FRAMEBUFFER. - * - * It is possible for a frontend to return a different pixel format - * than the one used in SET_PIXEL_FORMAT. This can happen if the frontend - * needs to perform conversion. - * - * It is still valid for a core to render to a different buffer - * even if GET_CURRENT_SOFTWARE_FRAMEBUFFER succeeds. - * - * A frontend must make sure that the pointer obtained from this function is - * writeable (and readable). - */ -#define RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE (41 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* const struct retro_hw_render_interface ** -- - * Returns an API specific rendering interface for accessing API specific data. - * Not all HW rendering APIs support or need this. - * The contents of the returned pointer is specific to the rendering API - * being used. See the various headers like libretro_vulkan.h, etc. - * - * GET_HW_RENDER_INTERFACE cannot be called before context_reset has been called. - * Similarly, after context_destroyed callback returns, - * the contents of the HW_RENDER_INTERFACE are invalidated. - */ -#define RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS (42 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* const bool * -- - * If true, the libretro implementation supports achievements - * either via memory descriptors set with RETRO_ENVIRONMENT_SET_MEMORY_MAPS - * or via retro_get_memory_data/retro_get_memory_size. - * - * This must be called before the first call to retro_run. - */ -#define RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE (43 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* const struct retro_hw_render_context_negotiation_interface * -- - * Sets an interface which lets the libretro core negotiate with frontend how a context is created. - * The semantics of this interface depends on which API is used in SET_HW_RENDER earlier. - * This interface will be used when the frontend is trying to create a HW rendering context, - * so it will be used after SET_HW_RENDER, but before the context_reset callback. - */ -#define RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS 44 - /* uint64_t * -- - * Sets quirk flags associated with serialization. The frontend will zero any flags it doesn't - * recognize or support. Should be set in either retro_init or retro_load_game, but not both. - */ -#define RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT (44 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* N/A (null) * -- - * The frontend will try to use a 'shared' hardware context (mostly applicable - * to OpenGL) when a hardware context is being set up. - * - * Returns true if the frontend supports shared hardware contexts and false - * if the frontend does not support shared hardware contexts. - * - * This will do nothing on its own until SET_HW_RENDER env callbacks are - * being used. - */ -#define RETRO_ENVIRONMENT_GET_VFS_INTERFACE (45 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* struct retro_vfs_interface_info * -- - * Gets access to the VFS interface. - * VFS presence needs to be queried prior to load_game or any - * get_system/save/other_directory being called to let front end know - * core supports VFS before it starts handing out paths. - * It is recomended to do so in retro_set_environment - */ -#define RETRO_ENVIRONMENT_GET_LED_INTERFACE (46 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* struct retro_led_interface * -- - * Gets an interface which is used by a libretro core to set - * state of LEDs. - */ -#define RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE (47 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* int * -- - * Tells the core if the frontend wants audio or video. - * If disabled, the frontend will discard the audio or video, - * so the core may decide to skip generating a frame or generating audio. - * This is mainly used for increasing performance. - * Bit 0 (value 1): Enable Video - * Bit 1 (value 2): Enable AUDIO - * Bit 2 (value 4): Use Fast Savestates. - * Bit 3 (value 8): Hard Disable AUDIO - * Other bits are reserved for future use and will default to zero. - * If video is disabled: - * * The frontend wants the core to not generate any video, - * including presenting frames via hardware acceleration. - * * The frontend's video frame callback will do nothing. - * * After running the frame, the video output of the next frame should be - * no different than if video was enabled, and saving and loading state - * should have no issues. - * If audio is disabled: - * * The frontend wants the core to not generate any audio. - * * The frontend's audio callbacks will do nothing. - * * After running the frame, the audio output of the next frame should be - * no different than if audio was enabled, and saving and loading state - * should have no issues. - * Fast Savestates: - * * Guaranteed to be created by the same binary that will load them. - * * Will not be written to or read from the disk. - * * Suggest that the core assumes loading state will succeed. - * * Suggest that the core updates its memory buffers in-place if possible. - * * Suggest that the core skips clearing memory. - * * Suggest that the core skips resetting the system. - * * Suggest that the core may skip validation steps. - * Hard Disable AUDIO: - * * Used for a secondary core when running ahead. - * * Indicates that the frontend will never need audio from the core. - * * Suggests that the core may stop synthesizing audio, but this should not - * compromise emulation accuracy. - * * AUDIO output for the next frame does not matter, and the frontend will - * never need an accurate audio state in the future. - * * State will never be saved when using Hard Disable AUDIO. - */ -#define RETRO_ENVIRONMENT_GET_MIDI_INTERFACE (48 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* struct retro_midi_interface ** -- - * Returns a MIDI interface that can be used for raw data I/O. - */ - -#define RETRO_ENVIRONMENT_GET_FASTFORWARDING (49 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* bool * -- - * Boolean value that indicates whether or not the frontend is in - * fastforwarding mode. - */ - -#define RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE (50 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* float * -- - * Float value that lets us know what target refresh rate - * is curently in use by the frontend. - * - * The core can use the returned value to set an ideal - * refresh rate/framerate. - */ - -#define RETRO_ENVIRONMENT_GET_INPUT_BITMASKS (51 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* bool * -- - * Boolean value that indicates whether or not the frontend supports - * input bitmasks being returned by retro_input_state_t. The advantage - * of this is that retro_input_state_t has to be only called once to - * grab all button states instead of multiple times. - * - * If it returns true, you can pass RETRO_DEVICE_ID_JOYPAD_MASK as 'id' - * to retro_input_state_t (make sure 'device' is set to RETRO_DEVICE_JOYPAD). - * It will return a bitmask of all the digital buttons. - */ - -#define RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION 52 - /* unsigned * -- - * Unsigned value is the API version number of the core options - * interface supported by the frontend. If callback return false, - * API version is assumed to be 0. - * - * In legacy code, core options are set by passing an array of - * retro_variable structs to RETRO_ENVIRONMENT_SET_VARIABLES. - * This may be still be done regardless of the core options - * interface version. - * - * If version is >= 1 however, core options may instead be set by - * passing an array of retro_core_option_definition structs to - * RETRO_ENVIRONMENT_SET_CORE_OPTIONS, or a 2D array of - * retro_core_option_definition structs to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL. - * This allows the core to additionally set option sublabel information - * and/or provide localisation support. - * - * If version is >= 2, core options may instead be set by passing - * a retro_core_options_v2 struct to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, - * or an array of retro_core_options_v2 structs to - * RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL. This allows the core - * to additionally set optional core option category information - * for frontends with core option category support. - */ - -#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS 53 - /* const struct retro_core_option_definition ** -- - * Allows an implementation to signal the environment - * which variables it might want to check for later using - * GET_VARIABLE. - * This allows the frontend to present these variables to - * a user dynamically. - * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION - * returns an API version of >= 1. - * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. - * This should be called the first time as early as - * possible (ideally in retro_set_environment). - * Afterwards it may be called again for the core to communicate - * updated options to the frontend, but the number of core - * options must not change from the number in the initial call. - * - * 'data' points to an array of retro_core_option_definition structs - * terminated by a { NULL, NULL, NULL, {{0}}, NULL } element. - * retro_core_option_definition::key should be namespaced to not collide - * with other implementations' keys. e.g. A core called - * 'foo' should use keys named as 'foo_option'. - * retro_core_option_definition::desc should contain a human readable - * description of the key. - * retro_core_option_definition::info should contain any additional human - * readable information text that a typical user may need to - * understand the functionality of the option. - * retro_core_option_definition::values is an array of retro_core_option_value - * structs terminated by a { NULL, NULL } element. - * > retro_core_option_definition::values[index].value is an expected option - * value. - * > retro_core_option_definition::values[index].label is a human readable - * label used when displaying the value on screen. If NULL, - * the value itself is used. - * retro_core_option_definition::default_value is the default core option - * setting. It must match one of the expected option values in the - * retro_core_option_definition::values array. If it does not, or the - * default value is NULL, the first entry in the - * retro_core_option_definition::values array is treated as the default. - * - * The number of possible option values should be very limited, - * and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX. - * i.e. it should be feasible to cycle through options - * without a keyboard. - * - * Example entry: - * { - * "foo_option", - * "Speed hack coprocessor X", - * "Provides increased performance at the expense of reduced accuracy", - * { - * { "false", NULL }, - * { "true", NULL }, - * { "unstable", "Turbo (Unstable)" }, - * { NULL, NULL }, - * }, - * "false" - * } - * - * Only strings are operated on. The possible values will - * generally be displayed and stored as-is by the frontend. - */ - -#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL 54 - /* const struct retro_core_options_intl * -- - * Allows an implementation to signal the environment - * which variables it might want to check for later using - * GET_VARIABLE. - * This allows the frontend to present these variables to - * a user dynamically. - * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION - * returns an API version of >= 1. - * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. - * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. - * This should be called the first time as early as - * possible (ideally in retro_set_environment). - * Afterwards it may be called again for the core to communicate - * updated options to the frontend, but the number of core - * options must not change from the number in the initial call. - * - * This is fundamentally the same as RETRO_ENVIRONMENT_SET_CORE_OPTIONS, - * with the addition of localisation support. The description of the - * RETRO_ENVIRONMENT_SET_CORE_OPTIONS callback should be consulted - * for further details. - * - * 'data' points to a retro_core_options_intl struct. - * - * retro_core_options_intl::us is a pointer to an array of - * retro_core_option_definition structs defining the US English - * core options implementation. It must point to a valid array. - * - * retro_core_options_intl::local is a pointer to an array of - * retro_core_option_definition structs defining core options for - * the current frontend language. It may be NULL (in which case - * retro_core_options_intl::us is used by the frontend). Any items - * missing from this array will be read from retro_core_options_intl::us - * instead. - * - * NOTE: Default core option values are always taken from the - * retro_core_options_intl::us array. Any default values in - * retro_core_options_intl::local array will be ignored. - */ - -#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY 55 - /* struct retro_core_option_display * -- - * - * Allows an implementation to signal the environment to show - * or hide a variable when displaying core options. This is - * considered a *suggestion*. The frontend is free to ignore - * this callback, and its implementation not considered mandatory. - * - * 'data' points to a retro_core_option_display struct - * - * retro_core_option_display::key is a variable identifier - * which has already been set by SET_VARIABLES/SET_CORE_OPTIONS. - * - * retro_core_option_display::visible is a boolean, specifying - * whether variable should be displayed - * - * Note that all core option variables will be set visible by - * default when calling SET_VARIABLES/SET_CORE_OPTIONS. - */ - -#define RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER 56 - /* unsigned * -- - * - * Allows an implementation to ask frontend preferred hardware - * context to use. Core should use this information to deal - * with what specific context to request with SET_HW_RENDER. - * - * 'data' points to an unsigned variable - */ - -#define RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION 57 - /* unsigned * -- - * Unsigned value is the API version number of the disk control - * interface supported by the frontend. If callback return false, - * API version is assumed to be 0. - * - * In legacy code, the disk control interface is defined by passing - * a struct of type retro_disk_control_callback to - * RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE. - * This may be still be done regardless of the disk control - * interface version. - * - * If version is >= 1 however, the disk control interface may - * instead be defined by passing a struct of type - * retro_disk_control_ext_callback to - * RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE. - * This allows the core to provide additional information about - * disk images to the frontend and/or enables extra - * disk control functionality by the frontend. - */ - -#define RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE 58 - /* const struct retro_disk_control_ext_callback * -- - * Sets an interface which frontend can use to eject and insert - * disk images, and also obtain information about individual - * disk image files registered by the core. - * This is used for games which consist of multiple images and - * must be manually swapped out by the user (e.g. PSX, floppy disk - * based systems). - */ - -#define RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION 59 - /* unsigned * -- - * Unsigned value is the API version number of the message - * interface supported by the frontend. If callback returns - * false, API version is assumed to be 0. - * - * In legacy code, messages may be displayed in an - * implementation-specific manner by passing a struct - * of type retro_message to RETRO_ENVIRONMENT_SET_MESSAGE. - * This may be still be done regardless of the message - * interface version. - * - * If version is >= 1 however, messages may instead be - * displayed by passing a struct of type retro_message_ext - * to RETRO_ENVIRONMENT_SET_MESSAGE_EXT. This allows the - * core to specify message logging level, priority and - * destination (OSD, logging interface or both). - */ - -#define RETRO_ENVIRONMENT_SET_MESSAGE_EXT 60 - /* const struct retro_message_ext * -- - * Sets a message to be displayed in an implementation-specific - * manner for a certain amount of 'frames'. Additionally allows - * the core to specify message logging level, priority and - * destination (OSD, logging interface or both). - * Should not be used for trivial messages, which should simply be - * logged via RETRO_ENVIRONMENT_GET_LOG_INTERFACE (or as a - * fallback, stderr). - */ - -#define RETRO_ENVIRONMENT_GET_INPUT_MAX_USERS 61 - /* unsigned * -- - * Unsigned value is the number of active input devices - * provided by the frontend. This may change between - * frames, but will remain constant for the duration - * of each frame. - * If callback returns true, a core need not poll any - * input device with an index greater than or equal to - * the number of active devices. - * If callback returns false, the number of active input - * devices is unknown. In this case, all input devices - * should be considered active. - */ - -#define RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK 62 - /* const struct retro_audio_buffer_status_callback * -- - * Lets the core know the occupancy level of the frontend - * audio buffer. Can be used by a core to attempt frame - * skipping in order to avoid buffer under-runs. - * A core may pass NULL to disable buffer status reporting - * in the frontend. - */ - -#define RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY 63 - /* const unsigned * -- - * Sets minimum frontend audio latency in milliseconds. - * Resultant audio latency may be larger than set value, - * or smaller if a hardware limit is encountered. A frontend - * is expected to honour requests up to 512 ms. - * - * - If value is less than current frontend - * audio latency, callback has no effect - * - If value is zero, default frontend audio - * latency is set - * - * May be used by a core to increase audio latency and - * therefore decrease the probability of buffer under-runs - * (crackling) when performing 'intensive' operations. - * A core utilising RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK - * to implement audio-buffer-based frame skipping may achieve - * optimal results by setting the audio latency to a 'high' - * (typically 6x or 8x) integer multiple of the expected - * frame time. - * - * WARNING: This can only be called from within retro_run(). - * Calling this can require a full reinitialization of audio - * drivers in the frontend, so it is important to call it very - * sparingly, and usually only with the users explicit consent. - * An eventual driver reinitialize will happen so that audio - * callbacks happening after this call within the same retro_run() - * call will target the newly initialized driver. - */ - -#define RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE 64 - /* const struct retro_fastforwarding_override * -- - * Used by a libretro core to override the current - * fastforwarding mode of the frontend. - * If NULL is passed to this function, the frontend - * will return true if fastforwarding override - * functionality is supported (no change in - * fastforwarding state will occur in this case). - */ - -#define RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE 65 - /* const struct retro_system_content_info_override * -- - * Allows an implementation to override 'global' content - * info parameters reported by retro_get_system_info(). - * Overrides also affect subsystem content info parameters - * set via RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO. - * This function must be called inside retro_set_environment(). - * If callback returns false, content info overrides - * are unsupported by the frontend, and will be ignored. - * If callback returns true, extended game info may be - * retrieved by calling RETRO_ENVIRONMENT_GET_GAME_INFO_EXT - * in retro_load_game() or retro_load_game_special(). - * - * 'data' points to an array of retro_system_content_info_override - * structs terminated by a { NULL, false, false } element. - * If 'data' is NULL, no changes will be made to the frontend; - * a core may therefore pass NULL in order to test whether - * the RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE and - * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT callbacks are supported - * by the frontend. - * - * For struct member descriptions, see the definition of - * struct retro_system_content_info_override. - * - * Example: - * - * - struct retro_system_info: - * { - * "My Core", // library_name - * "v1.0", // library_version - * "m3u|md|cue|iso|chd|sms|gg|sg", // valid_extensions - * true, // need_fullpath - * false // block_extract - * } - * - * - Array of struct retro_system_content_info_override: - * { - * { - * "md|sms|gg", // extensions - * false, // need_fullpath - * true // persistent_data - * }, - * { - * "sg", // extensions - * false, // need_fullpath - * false // persistent_data - * }, - * { NULL, false, false } - * } - * - * Result: - * - Files of type m3u, cue, iso, chd will not be - * loaded by the frontend. Frontend will pass a - * valid path to the core, and core will handle - * loading internally - * - Files of type md, sms, gg will be loaded by - * the frontend. A valid memory buffer will be - * passed to the core. This memory buffer will - * remain valid until retro_deinit() returns - * - Files of type sg will be loaded by the frontend. - * A valid memory buffer will be passed to the core. - * This memory buffer will remain valid until - * retro_load_game() (or retro_load_game_special()) - * returns - * - * NOTE: If an extension is listed multiple times in - * an array of retro_system_content_info_override - * structs, only the first instance will be registered - */ - -#define RETRO_ENVIRONMENT_GET_GAME_INFO_EXT 66 - /* const struct retro_game_info_ext ** -- - * Allows an implementation to fetch extended game - * information, providing additional content path - * and memory buffer status details. - * This function may only be called inside - * retro_load_game() or retro_load_game_special(). - * If callback returns false, extended game information - * is unsupported by the frontend. In this case, only - * regular retro_game_info will be available. - * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT is guaranteed - * to return true if RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE - * returns true. - * - * 'data' points to an array of retro_game_info_ext structs. - * - * For struct member descriptions, see the definition of - * struct retro_game_info_ext. - * - * - If function is called inside retro_load_game(), - * the retro_game_info_ext array is guaranteed to - * have a size of 1 - i.e. the returned pointer may - * be used to access directly the members of the - * first retro_game_info_ext struct, for example: - * - * struct retro_game_info_ext *game_info_ext; - * if (environ_cb(RETRO_ENVIRONMENT_GET_GAME_INFO_EXT, &game_info_ext)) - * printf("Content Directory: %s\n", game_info_ext->dir); - * - * - If the function is called inside retro_load_game_special(), - * the retro_game_info_ext array is guaranteed to have a - * size equal to the num_info argument passed to - * retro_load_game_special() - */ - -#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 67 - /* const struct retro_core_options_v2 * -- - * Allows an implementation to signal the environment - * which variables it might want to check for later using - * GET_VARIABLE. - * This allows the frontend to present these variables to - * a user dynamically. - * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION - * returns an API version of >= 2. - * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. - * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. - * This should be called the first time as early as - * possible (ideally in retro_set_environment). - * Afterwards it may be called again for the core to communicate - * updated options to the frontend, but the number of core - * options must not change from the number in the initial call. - * If RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION returns an API - * version of >= 2, this callback is guaranteed to succeed - * (i.e. callback return value does not indicate success) - * If callback returns true, frontend has core option category - * support. - * If callback returns false, frontend does not have core option - * category support. - * - * 'data' points to a retro_core_options_v2 struct, containing - * of two pointers: - * - retro_core_options_v2::categories is an array of - * retro_core_option_v2_category structs terminated by a - * { NULL, NULL, NULL } element. If retro_core_options_v2::categories - * is NULL, all core options will have no category and will be shown - * at the top level of the frontend core option interface. If frontend - * does not have core option category support, categories array will - * be ignored. - * - retro_core_options_v2::definitions is an array of - * retro_core_option_v2_definition structs terminated by a - * { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL } - * element. - * - * >> retro_core_option_v2_category notes: - * - * - retro_core_option_v2_category::key should contain string - * that uniquely identifies the core option category. Valid - * key characters are [a-z, A-Z, 0-9, _, -] - * Namespace collisions with other implementations' category - * keys are permitted. - * - retro_core_option_v2_category::desc should contain a human - * readable description of the category key. - * - retro_core_option_v2_category::info should contain any - * additional human readable information text that a typical - * user may need to understand the nature of the core option - * category. - * - * Example entry: - * { - * "advanced_settings", - * "Advanced", - * "Options affecting low-level emulation performance and accuracy." - * } - * - * >> retro_core_option_v2_definition notes: - * - * - retro_core_option_v2_definition::key should be namespaced to not - * collide with other implementations' keys. e.g. A core called - * 'foo' should use keys named as 'foo_option'. Valid key characters - * are [a-z, A-Z, 0-9, _, -]. - * - retro_core_option_v2_definition::desc should contain a human readable - * description of the key. Will be used when the frontend does not - * have core option category support. Examples: "Aspect Ratio" or - * "Video > Aspect Ratio". - * - retro_core_option_v2_definition::desc_categorized should contain a - * human readable description of the key, which will be used when - * frontend has core option category support. Example: "Aspect Ratio", - * where associated retro_core_option_v2_category::desc is "Video". - * If empty or NULL, the string specified by - * retro_core_option_v2_definition::desc will be used instead. - * retro_core_option_v2_definition::desc_categorized will be ignored - * if retro_core_option_v2_definition::category_key is empty or NULL. - * - retro_core_option_v2_definition::info should contain any additional - * human readable information text that a typical user may need to - * understand the functionality of the option. - * - retro_core_option_v2_definition::info_categorized should contain - * any additional human readable information text that a typical user - * may need to understand the functionality of the option, and will be - * used when frontend has core option category support. This is provided - * to accommodate the case where info text references an option by - * name/desc, and the desc/desc_categorized text for that option differ. - * If empty or NULL, the string specified by - * retro_core_option_v2_definition::info will be used instead. - * retro_core_option_v2_definition::info_categorized will be ignored - * if retro_core_option_v2_definition::category_key is empty or NULL. - * - retro_core_option_v2_definition::category_key should contain a - * category identifier (e.g. "video" or "audio") that will be - * assigned to the core option if frontend has core option category - * support. A categorized option will be shown in a subsection/ - * submenu of the frontend core option interface. If key is empty - * or NULL, or if key does not match one of the - * retro_core_option_v2_category::key values in the associated - * retro_core_option_v2_category array, option will have no category - * and will be shown at the top level of the frontend core option - * interface. - * - retro_core_option_v2_definition::values is an array of - * retro_core_option_value structs terminated by a { NULL, NULL } - * element. - * --> retro_core_option_v2_definition::values[index].value is an - * expected option value. - * --> retro_core_option_v2_definition::values[index].label is a - * human readable label used when displaying the value on screen. - * If NULL, the value itself is used. - * - retro_core_option_v2_definition::default_value is the default - * core option setting. It must match one of the expected option - * values in the retro_core_option_v2_definition::values array. If - * it does not, or the default value is NULL, the first entry in the - * retro_core_option_v2_definition::values array is treated as the - * default. - * - * The number of possible option values should be very limited, - * and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX. - * i.e. it should be feasible to cycle through options - * without a keyboard. - * - * Example entries: - * - * - Uncategorized: - * - * { - * "foo_option", - * "Speed hack coprocessor X", - * NULL, - * "Provides increased performance at the expense of reduced accuracy.", - * NULL, - * NULL, - * { - * { "false", NULL }, - * { "true", NULL }, - * { "unstable", "Turbo (Unstable)" }, - * { NULL, NULL }, - * }, - * "false" - * } - * - * - Categorized: - * - * { - * "foo_option", - * "Advanced > Speed hack coprocessor X", - * "Speed hack coprocessor X", - * "Setting 'Advanced > Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy", - * "Setting 'Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy", - * "advanced_settings", - * { - * { "false", NULL }, - * { "true", NULL }, - * { "unstable", "Turbo (Unstable)" }, - * { NULL, NULL }, - * }, - * "false" - * } - * - * Only strings are operated on. The possible values will - * generally be displayed and stored as-is by the frontend. - */ - -#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL 68 - /* const struct retro_core_options_v2_intl * -- - * Allows an implementation to signal the environment - * which variables it might want to check for later using - * GET_VARIABLE. - * This allows the frontend to present these variables to - * a user dynamically. - * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION - * returns an API version of >= 2. - * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. - * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. - * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL. - * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2. - * This should be called the first time as early as - * possible (ideally in retro_set_environment). - * Afterwards it may be called again for the core to communicate - * updated options to the frontend, but the number of core - * options must not change from the number in the initial call. - * If RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION returns an API - * version of >= 2, this callback is guaranteed to succeed - * (i.e. callback return value does not indicate success) - * If callback returns true, frontend has core option category - * support. - * If callback returns false, frontend does not have core option - * category support. - * - * This is fundamentally the same as RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, - * with the addition of localisation support. The description of the - * RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 callback should be consulted - * for further details. - * - * 'data' points to a retro_core_options_v2_intl struct. - * - * - retro_core_options_v2_intl::us is a pointer to a - * retro_core_options_v2 struct defining the US English - * core options implementation. It must point to a valid struct. - * - * - retro_core_options_v2_intl::local is a pointer to a - * retro_core_options_v2 struct defining core options for - * the current frontend language. It may be NULL (in which case - * retro_core_options_v2_intl::us is used by the frontend). Any items - * missing from this struct will be read from - * retro_core_options_v2_intl::us instead. - * - * NOTE: Default core option values are always taken from the - * retro_core_options_v2_intl::us struct. Any default values in - * the retro_core_options_v2_intl::local struct will be ignored. - */ - -#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK 69 - /* const struct retro_core_options_update_display_callback * -- - * Allows a frontend to signal that a core must update - * the visibility of any dynamically hidden core options, - * and enables the frontend to detect visibility changes. - * Used by the frontend to update the menu display status - * of core options without requiring a call of retro_run(). - * Must be called in retro_set_environment(). - */ - -#define RETRO_ENVIRONMENT_SET_VARIABLE 70 - /* const struct retro_variable * -- - * Allows an implementation to notify the frontend - * that a core option value has changed. - * - * retro_variable::key and retro_variable::value - * must match strings that have been set previously - * via one of the following: - * - * - RETRO_ENVIRONMENT_SET_VARIABLES - * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS - * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL - * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 - * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL - * - * After changing a core option value via this - * callback, RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE - * will return true. - * - * If data is NULL, no changes will be registered - * and the callback will return true; an - * implementation may therefore pass NULL in order - * to test whether the callback is supported. - */ - -#define RETRO_ENVIRONMENT_GET_THROTTLE_STATE (71 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* struct retro_throttle_state * -- - * Allows an implementation to get details on the actual rate - * the frontend is attempting to call retro_run(). - */ - -#define RETRO_ENVIRONMENT_GET_SAVESTATE_CONTEXT (72 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* int * -- - * Tells the core about the context the frontend is asking for savestate. - * (see enum retro_savestate_context) - */ - -#define RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT (73 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* struct retro_hw_render_context_negotiation_interface * -- - * Before calling SET_HW_RNEDER_CONTEXT_NEGOTIATION_INTERFACE, a core can query - * which version of the interface is supported. - * - * Frontend looks at interface_type and returns the maximum supported - * context negotiation interface version. - * If the interface_type is not supported or recognized by the frontend, a version of 0 - * must be returned in interface_version and true is returned by frontend. - * - * If this environment call returns true with interface_version greater than 0, - * a core can always use a negotiation interface version larger than what the frontend returns, but only - * earlier versions of the interface will be used by the frontend. - * A frontend must not reject a negotiation interface version that is larger than - * what the frontend supports. Instead, the frontend will use the older entry points that it recognizes. - * If this is incompatible with a particular core's requirements, it can error out early. - * - * Backwards compatibility note: - * This environment call was introduced after Vulkan v1 context negotiation. - * If this environment call is not supported by frontend - i.e. the environment call returns false - - * only Vulkan v1 context negotiation is supported (if Vulkan HW rendering is supported at all). - * If a core uses Vulkan negotiation interface with version > 1, negotiation may fail unexpectedly. - * All future updates to the context negotiation interface implies that frontend must support - * this environment call to query support. - */ - -#define RETRO_ENVIRONMENT_GET_JIT_CAPABLE 74 - /* bool * -- - * Result is set to true if the frontend has already verified JIT can be - * used, mainly for use iOS/tvOS. On other platforms the result is true. - */ - - -/* VFS functionality */ - -/* File paths: - * File paths passed as parameters when using this API shall be well formed UNIX-style, - * using "/" (unquoted forward slash) as directory separator regardless of the platform's native separator. - * Paths shall also include at least one forward slash ("game.bin" is an invalid path, use "./game.bin" instead). - * Other than the directory separator, cores shall not make assumptions about path format: - * "C:/path/game.bin", "http://example.com/game.bin", "#game/game.bin", "./game.bin" (without quotes) are all valid paths. - * Cores may replace the basename or remove path components from the end, and/or add new components; - * however, cores shall not append "./", "../" or multiple consecutive forward slashes ("//") to paths they request to front end. - * The frontend is encouraged to make such paths work as well as it can, but is allowed to give up if the core alters paths too much. - * Frontends are encouraged, but not required, to support native file system paths (modulo replacing the directory separator, if applicable). - * Cores are allowed to try using them, but must remain functional if the front rejects such requests. - * Cores are encouraged to use the libretro-common filestream functions for file I/O, - * as they seamlessly integrate with VFS, deal with directory separator replacement as appropriate - * and provide platform-specific fallbacks in cases where front ends do not support VFS. */ - -/* Opaque file handle - * Introduced in VFS API v1 */ -struct retro_vfs_file_handle; - -/* Opaque directory handle - * Introduced in VFS API v3 */ -struct retro_vfs_dir_handle; - -/* File open flags - * Introduced in VFS API v1 */ -#define RETRO_VFS_FILE_ACCESS_READ (1 << 0) /* Read only mode */ -#define RETRO_VFS_FILE_ACCESS_WRITE (1 << 1) /* Write only mode, discard contents and overwrites existing file unless RETRO_VFS_FILE_ACCESS_UPDATE is also specified */ -#define RETRO_VFS_FILE_ACCESS_READ_WRITE (RETRO_VFS_FILE_ACCESS_READ | RETRO_VFS_FILE_ACCESS_WRITE) /* Read-write mode, discard contents and overwrites existing file unless RETRO_VFS_FILE_ACCESS_UPDATE is also specified*/ -#define RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING (1 << 2) /* Prevents discarding content of existing files opened for writing */ - -/* These are only hints. The frontend may choose to ignore them. Other than RAM/CPU/etc use, - and how they react to unlikely external interference (for example someone else writing to that file, - or the file's server going down), behavior will not change. */ -#define RETRO_VFS_FILE_ACCESS_HINT_NONE (0) -/* Indicate that the file will be accessed many times. The frontend should aggressively cache everything. */ -#define RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS (1 << 0) - -/* Seek positions */ -#define RETRO_VFS_SEEK_POSITION_START 0 -#define RETRO_VFS_SEEK_POSITION_CURRENT 1 -#define RETRO_VFS_SEEK_POSITION_END 2 - -/* stat() result flags - * Introduced in VFS API v3 */ -#define RETRO_VFS_STAT_IS_VALID (1 << 0) -#define RETRO_VFS_STAT_IS_DIRECTORY (1 << 1) -#define RETRO_VFS_STAT_IS_CHARACTER_SPECIAL (1 << 2) - -/* Get path from opaque handle. Returns the exact same path passed to file_open when getting the handle - * Introduced in VFS API v1 */ -typedef const char *(RETRO_CALLCONV *retro_vfs_get_path_t)(struct retro_vfs_file_handle *stream); - -/* Open a file for reading or writing. If path points to a directory, this will - * fail. Returns the opaque file handle, or NULL for error. - * Introduced in VFS API v1 */ -typedef struct retro_vfs_file_handle *(RETRO_CALLCONV *retro_vfs_open_t)(const char *path, unsigned mode, unsigned hints); - -/* Close the file and release its resources. Must be called if open_file returns non-NULL. Returns 0 on success, -1 on failure. - * Whether the call succeeds ot not, the handle passed as parameter becomes invalid and should no longer be used. - * Introduced in VFS API v1 */ -typedef int (RETRO_CALLCONV *retro_vfs_close_t)(struct retro_vfs_file_handle *stream); - -/* Return the size of the file in bytes, or -1 for error. - * Introduced in VFS API v1 */ -typedef int64_t (RETRO_CALLCONV *retro_vfs_size_t)(struct retro_vfs_file_handle *stream); - -/* Truncate file to specified size. Returns 0 on success or -1 on error - * Introduced in VFS API v2 */ -typedef int64_t (RETRO_CALLCONV *retro_vfs_truncate_t)(struct retro_vfs_file_handle *stream, int64_t length); - -/* Get the current read / write position for the file. Returns -1 for error. - * Introduced in VFS API v1 */ -typedef int64_t (RETRO_CALLCONV *retro_vfs_tell_t)(struct retro_vfs_file_handle *stream); - -/* Set the current read/write position for the file. Returns the new position, -1 for error. - * Introduced in VFS API v1 */ -typedef int64_t (RETRO_CALLCONV *retro_vfs_seek_t)(struct retro_vfs_file_handle *stream, int64_t offset, int seek_position); - -/* Read data from a file. Returns the number of bytes read, or -1 for error. - * Introduced in VFS API v1 */ -typedef int64_t (RETRO_CALLCONV *retro_vfs_read_t)(struct retro_vfs_file_handle *stream, void *s, uint64_t len); - -/* Write data to a file. Returns the number of bytes written, or -1 for error. - * Introduced in VFS API v1 */ -typedef int64_t (RETRO_CALLCONV *retro_vfs_write_t)(struct retro_vfs_file_handle *stream, const void *s, uint64_t len); - -/* Flush pending writes to file, if using buffered IO. Returns 0 on sucess, or -1 on failure. - * Introduced in VFS API v1 */ -typedef int (RETRO_CALLCONV *retro_vfs_flush_t)(struct retro_vfs_file_handle *stream); - -/* Delete the specified file. Returns 0 on success, -1 on failure - * Introduced in VFS API v1 */ -typedef int (RETRO_CALLCONV *retro_vfs_remove_t)(const char *path); - -/* Rename the specified file. Returns 0 on success, -1 on failure - * Introduced in VFS API v1 */ -typedef int (RETRO_CALLCONV *retro_vfs_rename_t)(const char *old_path, const char *new_path); - -/* Stat the specified file. Retruns a bitmask of RETRO_VFS_STAT_* flags, none are set if path was not valid. - * Additionally stores file size in given variable, unless NULL is given. - * Introduced in VFS API v3 */ -typedef int (RETRO_CALLCONV *retro_vfs_stat_t)(const char *path, int32_t *size); - -/* Create the specified directory. Returns 0 on success, -1 on unknown failure, -2 if already exists. - * Introduced in VFS API v3 */ -typedef int (RETRO_CALLCONV *retro_vfs_mkdir_t)(const char *dir); - -/* Open the specified directory for listing. Returns the opaque dir handle, or NULL for error. - * Support for the include_hidden argument may vary depending on the platform. - * Introduced in VFS API v3 */ -typedef struct retro_vfs_dir_handle *(RETRO_CALLCONV *retro_vfs_opendir_t)(const char *dir, bool include_hidden); - -/* Read the directory entry at the current position, and move the read pointer to the next position. - * Returns true on success, false if already on the last entry. - * Introduced in VFS API v3 */ -typedef bool (RETRO_CALLCONV *retro_vfs_readdir_t)(struct retro_vfs_dir_handle *dirstream); - -/* Get the name of the last entry read. Returns a string on success, or NULL for error. - * The returned string pointer is valid until the next call to readdir or closedir. - * Introduced in VFS API v3 */ -typedef const char *(RETRO_CALLCONV *retro_vfs_dirent_get_name_t)(struct retro_vfs_dir_handle *dirstream); - -/* Check if the last entry read was a directory. Returns true if it was, false otherwise (or on error). - * Introduced in VFS API v3 */ -typedef bool (RETRO_CALLCONV *retro_vfs_dirent_is_dir_t)(struct retro_vfs_dir_handle *dirstream); - -/* Close the directory and release its resources. Must be called if opendir returns non-NULL. Returns 0 on success, -1 on failure. - * Whether the call succeeds ot not, the handle passed as parameter becomes invalid and should no longer be used. - * Introduced in VFS API v3 */ -typedef int (RETRO_CALLCONV *retro_vfs_closedir_t)(struct retro_vfs_dir_handle *dirstream); - -struct retro_vfs_interface -{ - /* VFS API v1 */ - retro_vfs_get_path_t get_path; - retro_vfs_open_t open; - retro_vfs_close_t close; - retro_vfs_size_t size; - retro_vfs_tell_t tell; - retro_vfs_seek_t seek; - retro_vfs_read_t read; - retro_vfs_write_t write; - retro_vfs_flush_t flush; - retro_vfs_remove_t remove; - retro_vfs_rename_t rename; - /* VFS API v2 */ - retro_vfs_truncate_t truncate; - /* VFS API v3 */ - retro_vfs_stat_t stat; - retro_vfs_mkdir_t mkdir; - retro_vfs_opendir_t opendir; - retro_vfs_readdir_t readdir; - retro_vfs_dirent_get_name_t dirent_get_name; - retro_vfs_dirent_is_dir_t dirent_is_dir; - retro_vfs_closedir_t closedir; -}; - -struct retro_vfs_interface_info -{ - /* Set by core: should this be higher than the version the front end supports, - * front end will return false in the RETRO_ENVIRONMENT_GET_VFS_INTERFACE call - * Introduced in VFS API v1 */ - uint32_t required_interface_version; - - /* Frontend writes interface pointer here. The frontend also sets the actual - * version, must be at least required_interface_version. - * Introduced in VFS API v1 */ - struct retro_vfs_interface *iface; -}; - -enum retro_hw_render_interface_type -{ - RETRO_HW_RENDER_INTERFACE_VULKAN = 0, - RETRO_HW_RENDER_INTERFACE_D3D9 = 1, - RETRO_HW_RENDER_INTERFACE_D3D10 = 2, - RETRO_HW_RENDER_INTERFACE_D3D11 = 3, - RETRO_HW_RENDER_INTERFACE_D3D12 = 4, - RETRO_HW_RENDER_INTERFACE_GSKIT_PS2 = 5, - RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX -}; - -/* Base struct. All retro_hw_render_interface_* types - * contain at least these fields. */ -struct retro_hw_render_interface -{ - enum retro_hw_render_interface_type interface_type; - unsigned interface_version; -}; - -typedef void (RETRO_CALLCONV *retro_set_led_state_t)(int led, int state); -struct retro_led_interface -{ - retro_set_led_state_t set_led_state; -}; - -/* Retrieves the current state of the MIDI input. - * Returns true if it's enabled, false otherwise. */ -typedef bool (RETRO_CALLCONV *retro_midi_input_enabled_t)(void); - -/* Retrieves the current state of the MIDI output. - * Returns true if it's enabled, false otherwise */ -typedef bool (RETRO_CALLCONV *retro_midi_output_enabled_t)(void); - -/* Reads next byte from the input stream. - * Returns true if byte is read, false otherwise. */ -typedef bool (RETRO_CALLCONV *retro_midi_read_t)(uint8_t *byte); - -/* Writes byte to the output stream. - * 'delta_time' is in microseconds and represent time elapsed since previous write. - * Returns true if byte is written, false otherwise. */ -typedef bool (RETRO_CALLCONV *retro_midi_write_t)(uint8_t byte, uint32_t delta_time); - -/* Flushes previously written data. - * Returns true if successful, false otherwise. */ -typedef bool (RETRO_CALLCONV *retro_midi_flush_t)(void); - -struct retro_midi_interface -{ - retro_midi_input_enabled_t input_enabled; - retro_midi_output_enabled_t output_enabled; - retro_midi_read_t read; - retro_midi_write_t write; - retro_midi_flush_t flush; -}; - -enum retro_hw_render_context_negotiation_interface_type -{ - RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN = 0, - RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_DUMMY = INT_MAX -}; - -/* Base struct. All retro_hw_render_context_negotiation_interface_* types - * contain at least these fields. */ -struct retro_hw_render_context_negotiation_interface -{ - enum retro_hw_render_context_negotiation_interface_type interface_type; - unsigned interface_version; -}; - -/* Serialized state is incomplete in some way. Set if serialization is - * usable in typical end-user cases but should not be relied upon to - * implement frame-sensitive frontend features such as netplay or - * rerecording. */ -#define RETRO_SERIALIZATION_QUIRK_INCOMPLETE (1 << 0) -/* The core must spend some time initializing before serialization is - * supported. retro_serialize() will initially fail; retro_unserialize() - * and retro_serialize_size() may or may not work correctly either. */ -#define RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE (1 << 1) -/* Serialization size may change within a session. */ -#define RETRO_SERIALIZATION_QUIRK_CORE_VARIABLE_SIZE (1 << 2) -/* Set by the frontend to acknowledge that it supports variable-sized - * states. */ -#define RETRO_SERIALIZATION_QUIRK_FRONT_VARIABLE_SIZE (1 << 3) -/* Serialized state can only be loaded during the same session. */ -#define RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION (1 << 4) -/* Serialized state cannot be loaded on an architecture with a different - * endianness from the one it was saved on. */ -#define RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT (1 << 5) -/* Serialized state cannot be loaded on a different platform from the one it - * was saved on for reasons other than endianness, such as word size - * dependence */ -#define RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT (1 << 6) - -#define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */ -#define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */ -#define RETRO_MEMDESC_SYSTEM_RAM (1 << 2) /* The memory area is system RAM. This is main RAM of the gaming system. */ -#define RETRO_MEMDESC_SAVE_RAM (1 << 3) /* The memory area is save RAM. This RAM is usually found on a game cartridge, backed up by a battery. */ -#define RETRO_MEMDESC_VIDEO_RAM (1 << 4) /* The memory area is video RAM (VRAM) */ -#define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */ -#define RETRO_MEMDESC_ALIGN_4 (2 << 16) -#define RETRO_MEMDESC_ALIGN_8 (3 << 16) -#define RETRO_MEMDESC_MINSIZE_2 (1 << 24) /* All memory in this region is accessed at least 2 bytes at the time. */ -#define RETRO_MEMDESC_MINSIZE_4 (2 << 24) -#define RETRO_MEMDESC_MINSIZE_8 (3 << 24) -struct retro_memory_descriptor -{ - uint64_t flags; - - /* Pointer to the start of the relevant ROM or RAM chip. - * It's strongly recommended to use 'offset' if possible, rather than - * doing math on the pointer. - * - * If the same byte is mapped my multiple descriptors, their descriptors - * must have the same pointer. - * If 'start' does not point to the first byte in the pointer, put the - * difference in 'offset' instead. - * - * May be NULL if there's nothing usable here (e.g. hardware registers and - * open bus). No flags should be set if the pointer is NULL. - * It's recommended to minimize the number of descriptors if possible, - * but not mandatory. */ - void *ptr; - size_t offset; - - /* This is the location in the emulated address space - * where the mapping starts. */ - size_t start; - - /* Which bits must be same as in 'start' for this mapping to apply. - * The first memory descriptor to claim a certain byte is the one - * that applies. - * A bit which is set in 'start' must also be set in this. - * Can be zero, in which case each byte is assumed mapped exactly once. - * In this case, 'len' must be a power of two. */ - size_t select; - - /* If this is nonzero, the set bits are assumed not connected to the - * memory chip's address pins. */ - size_t disconnect; - - /* This one tells the size of the current memory area. - * If, after start+disconnect are applied, the address is higher than - * this, the highest bit of the address is cleared. - * - * If the address is still too high, the next highest bit is cleared. - * Can be zero, in which case it's assumed to be infinite (as limited - * by 'select' and 'disconnect'). */ - size_t len; - - /* To go from emulated address to physical address, the following - * order applies: - * Subtract 'start', pick off 'disconnect', apply 'len', add 'offset'. */ - - /* The address space name must consist of only a-zA-Z0-9_-, - * should be as short as feasible (maximum length is 8 plus the NUL), - * and may not be any other address space plus one or more 0-9A-F - * at the end. - * However, multiple memory descriptors for the same address space is - * allowed, and the address space name can be empty. NULL is treated - * as empty. - * - * Address space names are case sensitive, but avoid lowercase if possible. - * The same pointer may exist in multiple address spaces. - * - * Examples: - * blank+blank - valid (multiple things may be mapped in the same namespace) - * 'Sp'+'Sp' - valid (multiple things may be mapped in the same namespace) - * 'A'+'B' - valid (neither is a prefix of each other) - * 'S'+blank - valid ('S' is not in 0-9A-F) - * 'a'+blank - valid ('a' is not in 0-9A-F) - * 'a'+'A' - valid (neither is a prefix of each other) - * 'AR'+blank - valid ('R' is not in 0-9A-F) - * 'ARB'+blank - valid (the B can't be part of the address either, because - * there is no namespace 'AR') - * blank+'B' - not valid, because it's ambigous which address space B1234 - * would refer to. - * The length can't be used for that purpose; the frontend may want - * to append arbitrary data to an address, without a separator. */ - const char *addrspace; - - /* TODO: When finalizing this one, add a description field, which should be - * "WRAM" or something roughly equally long. */ - - /* TODO: When finalizing this one, replace 'select' with 'limit', which tells - * which bits can vary and still refer to the same address (limit = ~select). - * TODO: limit? range? vary? something else? */ - - /* TODO: When finalizing this one, if 'len' is above what 'select' (or - * 'limit') allows, it's bankswitched. Bankswitched data must have both 'len' - * and 'select' != 0, and the mappings don't tell how the system switches the - * banks. */ - - /* TODO: When finalizing this one, fix the 'len' bit removal order. - * For len=0x1800, pointer 0x1C00 should go to 0x1400, not 0x0C00. - * Algorithm: Take bits highest to lowest, but if it goes above len, clear - * the most recent addition and continue on the next bit. - * TODO: Can the above be optimized? Is "remove the lowest bit set in both - * pointer and 'len'" equivalent? */ - - /* TODO: Some emulators (MAME?) emulate big endian systems by only accessing - * the emulated memory in 32-bit chunks, native endian. But that's nothing - * compared to Darek Mihocka - * (section Emulation 103 - Nearly Free Byte Reversal) - he flips the ENTIRE - * RAM backwards! I'll want to represent both of those, via some flags. - * - * I suspect MAME either didn't think of that idea, or don't want the #ifdef. - * Not sure which, nor do I really care. */ - - /* TODO: Some of those flags are unused and/or don't really make sense. Clean - * them up. */ -}; - -/* The frontend may use the largest value of 'start'+'select' in a - * certain namespace to infer the size of the address space. - * - * If the address space is larger than that, a mapping with .ptr=NULL - * should be at the end of the array, with .select set to all ones for - * as long as the address space is big. - * - * Sample descriptors (minus .ptr, and RETRO_MEMFLAG_ on the flags): - * SNES WRAM: - * .start=0x7E0000, .len=0x20000 - * (Note that this must be mapped before the ROM in most cases; some of the - * ROM mappers - * try to claim $7E0000, or at least $7E8000.) - * SNES SPC700 RAM: - * .addrspace="S", .len=0x10000 - * SNES WRAM mirrors: - * .flags=MIRROR, .start=0x000000, .select=0xC0E000, .len=0x2000 - * .flags=MIRROR, .start=0x800000, .select=0xC0E000, .len=0x2000 - * SNES WRAM mirrors, alternate equivalent descriptor: - * .flags=MIRROR, .select=0x40E000, .disconnect=~0x1FFF - * (Various similar constructions can be created by combining parts of - * the above two.) - * SNES LoROM (512KB, mirrored a couple of times): - * .flags=CONST, .start=0x008000, .select=0x408000, .disconnect=0x8000, .len=512*1024 - * .flags=CONST, .start=0x400000, .select=0x400000, .disconnect=0x8000, .len=512*1024 - * SNES HiROM (4MB): - * .flags=CONST, .start=0x400000, .select=0x400000, .len=4*1024*1024 - * .flags=CONST, .offset=0x8000, .start=0x008000, .select=0x408000, .len=4*1024*1024 - * SNES ExHiROM (8MB): - * .flags=CONST, .offset=0, .start=0xC00000, .select=0xC00000, .len=4*1024*1024 - * .flags=CONST, .offset=4*1024*1024, .start=0x400000, .select=0xC00000, .len=4*1024*1024 - * .flags=CONST, .offset=0x8000, .start=0x808000, .select=0xC08000, .len=4*1024*1024 - * .flags=CONST, .offset=4*1024*1024+0x8000, .start=0x008000, .select=0xC08000, .len=4*1024*1024 - * Clarify the size of the address space: - * .ptr=NULL, .select=0xFFFFFF - * .len can be implied by .select in many of them, but was included for clarity. - */ - -struct retro_memory_map -{ - const struct retro_memory_descriptor *descriptors; - unsigned num_descriptors; -}; - -struct retro_controller_description -{ - /* Human-readable description of the controller. Even if using a generic - * input device type, this can be set to the particular device type the - * core uses. */ - const char *desc; - - /* Device type passed to retro_set_controller_port_device(). If the device - * type is a sub-class of a generic input device type, use the - * RETRO_DEVICE_SUBCLASS macro to create an ID. - * - * E.g. RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1). */ - unsigned id; -}; - -struct retro_controller_info -{ - const struct retro_controller_description *types; - unsigned num_types; -}; - -struct retro_subsystem_memory_info -{ - /* The extension associated with a memory type, e.g. "psram". */ - const char *extension; - - /* The memory type for retro_get_memory(). This should be at - * least 0x100 to avoid conflict with standardized - * libretro memory types. */ - unsigned type; -}; - -struct retro_subsystem_rom_info -{ - /* Describes what the content is (SGB BIOS, GB ROM, etc). */ - const char *desc; - - /* Same definition as retro_get_system_info(). */ - const char *valid_extensions; - - /* Same definition as retro_get_system_info(). */ - bool need_fullpath; - - /* Same definition as retro_get_system_info(). */ - bool block_extract; - - /* This is set if the content is required to load a game. - * If this is set to false, a zeroed-out retro_game_info can be passed. */ - bool required; - - /* Content can have multiple associated persistent - * memory types (retro_get_memory()). */ - const struct retro_subsystem_memory_info *memory; - unsigned num_memory; -}; - -struct retro_subsystem_info -{ - /* Human-readable string of the subsystem type, e.g. "Super GameBoy" */ - const char *desc; - - /* A computer friendly short string identifier for the subsystem type. - * This name must be [a-z]. - * E.g. if desc is "Super GameBoy", this can be "sgb". - * This identifier can be used for command-line interfaces, etc. - */ - const char *ident; - - /* Infos for each content file. The first entry is assumed to be the - * "most significant" content for frontend purposes. - * E.g. with Super GameBoy, the first content should be the GameBoy ROM, - * as it is the most "significant" content to a user. - * If a frontend creates new file paths based on the content used - * (e.g. savestates), it should use the path for the first ROM to do so. */ - const struct retro_subsystem_rom_info *roms; - - /* Number of content files associated with a subsystem. */ - unsigned num_roms; - - /* The type passed to retro_load_game_special(). */ - unsigned id; -}; - -typedef void (RETRO_CALLCONV *retro_proc_address_t)(void); - -/* libretro API extension functions: - * (None here so far). - * - * Get a symbol from a libretro core. - * Cores should only return symbols which are actual - * extensions to the libretro API. - * - * Frontends should not use this to obtain symbols to standard - * libretro entry points (static linking or dlsym). - * - * The symbol name must be equal to the function name, - * e.g. if void retro_foo(void); exists, the symbol must be called "retro_foo". - * The returned function pointer must be cast to the corresponding type. - */ -typedef retro_proc_address_t (RETRO_CALLCONV *retro_get_proc_address_t)(const char *sym); - -struct retro_get_proc_address_interface -{ - retro_get_proc_address_t get_proc_address; -}; - -enum retro_log_level -{ - RETRO_LOG_DEBUG = 0, - RETRO_LOG_INFO, - RETRO_LOG_WARN, - RETRO_LOG_ERROR, - - RETRO_LOG_DUMMY = INT_MAX -}; - -/* Logging function. Takes log level argument as well. */ -typedef void (RETRO_CALLCONV *retro_log_printf_t)(enum retro_log_level level, - const char *fmt, ...); - -struct retro_log_callback -{ - retro_log_printf_t log; -}; - -/* Performance related functions */ - -/* ID values for SIMD CPU features */ -#define RETRO_SIMD_SSE (1 << 0) -#define RETRO_SIMD_SSE2 (1 << 1) -#define RETRO_SIMD_VMX (1 << 2) -#define RETRO_SIMD_VMX128 (1 << 3) -#define RETRO_SIMD_AVX (1 << 4) -#define RETRO_SIMD_NEON (1 << 5) -#define RETRO_SIMD_SSE3 (1 << 6) -#define RETRO_SIMD_SSSE3 (1 << 7) -#define RETRO_SIMD_MMX (1 << 8) -#define RETRO_SIMD_MMXEXT (1 << 9) -#define RETRO_SIMD_SSE4 (1 << 10) -#define RETRO_SIMD_SSE42 (1 << 11) -#define RETRO_SIMD_AVX2 (1 << 12) -#define RETRO_SIMD_VFPU (1 << 13) -#define RETRO_SIMD_PS (1 << 14) -#define RETRO_SIMD_AES (1 << 15) -#define RETRO_SIMD_VFPV3 (1 << 16) -#define RETRO_SIMD_VFPV4 (1 << 17) -#define RETRO_SIMD_POPCNT (1 << 18) -#define RETRO_SIMD_MOVBE (1 << 19) -#define RETRO_SIMD_CMOV (1 << 20) -#define RETRO_SIMD_ASIMD (1 << 21) - -typedef uint64_t retro_perf_tick_t; -typedef int64_t retro_time_t; - -struct retro_perf_counter -{ - const char *ident; - retro_perf_tick_t start; - retro_perf_tick_t total; - retro_perf_tick_t call_cnt; - - bool registered; -}; - -/* Returns current time in microseconds. - * Tries to use the most accurate timer available. - */ -typedef retro_time_t (RETRO_CALLCONV *retro_perf_get_time_usec_t)(void); - -/* A simple counter. Usually nanoseconds, but can also be CPU cycles. - * Can be used directly if desired (when creating a more sophisticated - * performance counter system). - * */ -typedef retro_perf_tick_t (RETRO_CALLCONV *retro_perf_get_counter_t)(void); - -/* Returns a bit-mask of detected CPU features (RETRO_SIMD_*). */ -typedef uint64_t (RETRO_CALLCONV *retro_get_cpu_features_t)(void); - -/* Asks frontend to log and/or display the state of performance counters. - * Performance counters can always be poked into manually as well. - */ -typedef void (RETRO_CALLCONV *retro_perf_log_t)(void); - -/* Register a performance counter. - * ident field must be set with a discrete value and other values in - * retro_perf_counter must be 0. - * Registering can be called multiple times. To avoid calling to - * frontend redundantly, you can check registered field first. */ -typedef void (RETRO_CALLCONV *retro_perf_register_t)(struct retro_perf_counter *counter); - -/* Starts a registered counter. */ -typedef void (RETRO_CALLCONV *retro_perf_start_t)(struct retro_perf_counter *counter); - -/* Stops a registered counter. */ -typedef void (RETRO_CALLCONV *retro_perf_stop_t)(struct retro_perf_counter *counter); - -/* For convenience it can be useful to wrap register, start and stop in macros. - * E.g.: - * #ifdef LOG_PERFORMANCE - * #define RETRO_PERFORMANCE_INIT(perf_cb, name) static struct retro_perf_counter name = {#name}; if (!name.registered) perf_cb.perf_register(&(name)) - * #define RETRO_PERFORMANCE_START(perf_cb, name) perf_cb.perf_start(&(name)) - * #define RETRO_PERFORMANCE_STOP(perf_cb, name) perf_cb.perf_stop(&(name)) - * #else - * ... Blank macros ... - * #endif - * - * These can then be used mid-functions around code snippets. - * - * extern struct retro_perf_callback perf_cb; * Somewhere in the core. - * - * void do_some_heavy_work(void) - * { - * RETRO_PERFORMANCE_INIT(cb, work_1; - * RETRO_PERFORMANCE_START(cb, work_1); - * heavy_work_1(); - * RETRO_PERFORMANCE_STOP(cb, work_1); - * - * RETRO_PERFORMANCE_INIT(cb, work_2); - * RETRO_PERFORMANCE_START(cb, work_2); - * heavy_work_2(); - * RETRO_PERFORMANCE_STOP(cb, work_2); - * } - * - * void retro_deinit(void) - * { - * perf_cb.perf_log(); * Log all perf counters here for example. - * } - */ - -struct retro_perf_callback -{ - retro_perf_get_time_usec_t get_time_usec; - retro_get_cpu_features_t get_cpu_features; - - retro_perf_get_counter_t get_perf_counter; - retro_perf_register_t perf_register; - retro_perf_start_t perf_start; - retro_perf_stop_t perf_stop; - retro_perf_log_t perf_log; -}; - -/* FIXME: Document the sensor API and work out behavior. - * It will be marked as experimental until then. - */ -enum retro_sensor_action -{ - RETRO_SENSOR_ACCELEROMETER_ENABLE = 0, - RETRO_SENSOR_ACCELEROMETER_DISABLE, - RETRO_SENSOR_GYROSCOPE_ENABLE, - RETRO_SENSOR_GYROSCOPE_DISABLE, - RETRO_SENSOR_ILLUMINANCE_ENABLE, - RETRO_SENSOR_ILLUMINANCE_DISABLE, - - RETRO_SENSOR_DUMMY = INT_MAX -}; - -/* Id values for SENSOR types. */ -#define RETRO_SENSOR_ACCELEROMETER_X 0 -#define RETRO_SENSOR_ACCELEROMETER_Y 1 -#define RETRO_SENSOR_ACCELEROMETER_Z 2 -#define RETRO_SENSOR_GYROSCOPE_X 3 -#define RETRO_SENSOR_GYROSCOPE_Y 4 -#define RETRO_SENSOR_GYROSCOPE_Z 5 -#define RETRO_SENSOR_ILLUMINANCE 6 - -typedef bool (RETRO_CALLCONV *retro_set_sensor_state_t)(unsigned port, - enum retro_sensor_action action, unsigned rate); - -typedef float (RETRO_CALLCONV *retro_sensor_get_input_t)(unsigned port, unsigned id); - -struct retro_sensor_interface -{ - retro_set_sensor_state_t set_sensor_state; - retro_sensor_get_input_t get_sensor_input; -}; - -enum retro_camera_buffer -{ - RETRO_CAMERA_BUFFER_OPENGL_TEXTURE = 0, - RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER, - - RETRO_CAMERA_BUFFER_DUMMY = INT_MAX -}; - -/* Starts the camera driver. Can only be called in retro_run(). */ -typedef bool (RETRO_CALLCONV *retro_camera_start_t)(void); - -/* Stops the camera driver. Can only be called in retro_run(). */ -typedef void (RETRO_CALLCONV *retro_camera_stop_t)(void); - -/* Callback which signals when the camera driver is initialized - * and/or deinitialized. - * retro_camera_start_t can be called in initialized callback. - */ -typedef void (RETRO_CALLCONV *retro_camera_lifetime_status_t)(void); - -/* A callback for raw framebuffer data. buffer points to an XRGB8888 buffer. - * Width, height and pitch are similar to retro_video_refresh_t. - * First pixel is top-left origin. - */ -typedef void (RETRO_CALLCONV *retro_camera_frame_raw_framebuffer_t)(const uint32_t *buffer, - unsigned width, unsigned height, size_t pitch); - -/* A callback for when OpenGL textures are used. - * - * texture_id is a texture owned by camera driver. - * Its state or content should be considered immutable, except for things like - * texture filtering and clamping. - * - * texture_target is the texture target for the GL texture. - * These can include e.g. GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE, and possibly - * more depending on extensions. - * - * affine points to a packed 3x3 column-major matrix used to apply an affine - * transform to texture coordinates. (affine_matrix * vec3(coord_x, coord_y, 1.0)) - * After transform, normalized texture coord (0, 0) should be bottom-left - * and (1, 1) should be top-right (or (width, height) for RECTANGLE). - * - * GL-specific typedefs are avoided here to avoid relying on gl.h in - * the API definition. - */ -typedef void (RETRO_CALLCONV *retro_camera_frame_opengl_texture_t)(unsigned texture_id, - unsigned texture_target, const float *affine); - -struct retro_camera_callback -{ - /* Set by libretro core. - * Example bitmask: caps = (1 << RETRO_CAMERA_BUFFER_OPENGL_TEXTURE) | (1 << RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER). - */ - uint64_t caps; - - /* Desired resolution for camera. Is only used as a hint. */ - unsigned width; - unsigned height; - - /* Set by frontend. */ - retro_camera_start_t start; - retro_camera_stop_t stop; - - /* Set by libretro core if raw framebuffer callbacks will be used. */ - retro_camera_frame_raw_framebuffer_t frame_raw_framebuffer; - - /* Set by libretro core if OpenGL texture callbacks will be used. */ - retro_camera_frame_opengl_texture_t frame_opengl_texture; - - /* Set by libretro core. Called after camera driver is initialized and - * ready to be started. - * Can be NULL, in which this callback is not called. - */ - retro_camera_lifetime_status_t initialized; - - /* Set by libretro core. Called right before camera driver is - * deinitialized. - * Can be NULL, in which this callback is not called. - */ - retro_camera_lifetime_status_t deinitialized; -}; - -/* Sets the interval of time and/or distance at which to update/poll - * location-based data. - * - * To ensure compatibility with all location-based implementations, - * values for both interval_ms and interval_distance should be provided. - * - * interval_ms is the interval expressed in milliseconds. - * interval_distance is the distance interval expressed in meters. - */ -typedef void (RETRO_CALLCONV *retro_location_set_interval_t)(unsigned interval_ms, - unsigned interval_distance); - -/* Start location services. The device will start listening for changes to the - * current location at regular intervals (which are defined with - * retro_location_set_interval_t). */ -typedef bool (RETRO_CALLCONV *retro_location_start_t)(void); - -/* Stop location services. The device will stop listening for changes - * to the current location. */ -typedef void (RETRO_CALLCONV *retro_location_stop_t)(void); - -/* Get the position of the current location. Will set parameters to - * 0 if no new location update has happened since the last time. */ -typedef bool (RETRO_CALLCONV *retro_location_get_position_t)(double *lat, double *lon, - double *horiz_accuracy, double *vert_accuracy); - -/* Callback which signals when the location driver is initialized - * and/or deinitialized. - * retro_location_start_t can be called in initialized callback. - */ -typedef void (RETRO_CALLCONV *retro_location_lifetime_status_t)(void); - -struct retro_location_callback -{ - retro_location_start_t start; - retro_location_stop_t stop; - retro_location_get_position_t get_position; - retro_location_set_interval_t set_interval; - - retro_location_lifetime_status_t initialized; - retro_location_lifetime_status_t deinitialized; -}; - -enum retro_rumble_effect -{ - RETRO_RUMBLE_STRONG = 0, - RETRO_RUMBLE_WEAK = 1, - - RETRO_RUMBLE_DUMMY = INT_MAX -}; - -/* Sets rumble state for joypad plugged in port 'port'. - * Rumble effects are controlled independently, - * and setting e.g. strong rumble does not override weak rumble. - * Strength has a range of [0, 0xffff]. - * - * Returns true if rumble state request was honored. - * Calling this before first retro_run() is likely to return false. */ -typedef bool (RETRO_CALLCONV *retro_set_rumble_state_t)(unsigned port, - enum retro_rumble_effect effect, uint16_t strength); - -struct retro_rumble_interface -{ - retro_set_rumble_state_t set_rumble_state; -}; - -/* Notifies libretro that audio data should be written. */ -typedef void (RETRO_CALLCONV *retro_audio_callback_t)(void); - -/* True: AUDIO driver in frontend is active, and callback is - * expected to be called regularily. - * False: AUDIO driver in frontend is paused or inactive. - * AUDIO callback will not be called until set_state has been - * called with true. - * Initial state is false (inactive). - */ -typedef void (RETRO_CALLCONV *retro_audio_set_state_callback_t)(bool enabled); - -struct retro_audio_callback -{ - retro_audio_callback_t callback; - retro_audio_set_state_callback_t set_state; -}; - -/* Notifies a libretro core of time spent since last invocation - * of retro_run() in microseconds. - * - * It will be called right before retro_run() every frame. - * The frontend can tamper with timing to support cases like - * fast-forward, slow-motion and framestepping. - * - * In those scenarios the reference frame time value will be used. */ -typedef int64_t retro_usec_t; -typedef void (RETRO_CALLCONV *retro_frame_time_callback_t)(retro_usec_t usec); -struct retro_frame_time_callback -{ - retro_frame_time_callback_t callback; - /* Represents the time of one frame. It is computed as - * 1000000 / fps, but the implementation will resolve the - * rounding to ensure that framestepping, etc is exact. */ - retro_usec_t reference; -}; - -/* Notifies a libretro core of the current occupancy - * level of the frontend audio buffer. - * - * - active: 'true' if audio buffer is currently - * in use. Will be 'false' if audio is - * disabled in the frontend - * - * - occupancy: Given as a value in the range [0,100], - * corresponding to the occupancy percentage - * of the audio buffer - * - * - underrun_likely: 'true' if the frontend expects an - * audio buffer underrun during the - * next frame (indicates that a core - * should attempt frame skipping) - * - * It will be called right before retro_run() every frame. */ -typedef void (RETRO_CALLCONV *retro_audio_buffer_status_callback_t)( - bool active, unsigned occupancy, bool underrun_likely); -struct retro_audio_buffer_status_callback -{ - retro_audio_buffer_status_callback_t callback; -}; - -/* Pass this to retro_video_refresh_t if rendering to hardware. - * Passing NULL to retro_video_refresh_t is still a frame dupe as normal. - * */ -#define RETRO_HW_FRAME_BUFFER_VALID ((void*)-1) - -/* Invalidates the current HW context. - * Any GL state is lost, and must not be deinitialized explicitly. - * If explicit deinitialization is desired by the libretro core, - * it should implement context_destroy callback. - * If called, all GPU resources must be reinitialized. - * Usually called when frontend reinits video driver. - * Also called first time video driver is initialized, - * allowing libretro core to initialize resources. - */ -typedef void (RETRO_CALLCONV *retro_hw_context_reset_t)(void); - -/* Gets current framebuffer which is to be rendered to. - * Could change every frame potentially. - */ -typedef uintptr_t (RETRO_CALLCONV *retro_hw_get_current_framebuffer_t)(void); - -/* Get a symbol from HW context. */ -typedef retro_proc_address_t (RETRO_CALLCONV *retro_hw_get_proc_address_t)(const char *sym); - -enum retro_hw_context_type -{ - RETRO_HW_CONTEXT_NONE = 0, - /* OpenGL 2.x. Driver can choose to use latest compatibility context. */ - RETRO_HW_CONTEXT_OPENGL = 1, - /* OpenGL ES 2.0. */ - RETRO_HW_CONTEXT_OPENGLES2 = 2, - /* Modern desktop core GL context. Use version_major/ - * version_minor fields to set GL version. */ - RETRO_HW_CONTEXT_OPENGL_CORE = 3, - /* OpenGL ES 3.0 */ - RETRO_HW_CONTEXT_OPENGLES3 = 4, - /* OpenGL ES 3.1+. Set version_major/version_minor. For GLES2 and GLES3, - * use the corresponding enums directly. */ - RETRO_HW_CONTEXT_OPENGLES_VERSION = 5, - - /* Vulkan, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE. */ - RETRO_HW_CONTEXT_VULKAN = 6, - - /* Direct3D, set version_major to select the type of interface - * returned by RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */ - RETRO_HW_CONTEXT_DIRECT3D = 7, - - RETRO_HW_CONTEXT_DUMMY = INT_MAX -}; - -struct retro_hw_render_callback -{ - /* Which API to use. Set by libretro core. */ - enum retro_hw_context_type context_type; - - /* Called when a context has been created or when it has been reset. - * An OpenGL context is only valid after context_reset() has been called. - * - * When context_reset is called, OpenGL resources in the libretro - * implementation are guaranteed to be invalid. - * - * It is possible that context_reset is called multiple times during an - * application lifecycle. - * If context_reset is called without any notification (context_destroy), - * the OpenGL context was lost and resources should just be recreated - * without any attempt to "free" old resources. - */ - retro_hw_context_reset_t context_reset; - - /* Set by frontend. - * TODO: This is rather obsolete. The frontend should not - * be providing preallocated framebuffers. */ - retro_hw_get_current_framebuffer_t get_current_framebuffer; - - /* Set by frontend. - * Can return all relevant functions, including glClear on Windows. */ - retro_hw_get_proc_address_t get_proc_address; - - /* Set if render buffers should have depth component attached. - * TODO: Obsolete. */ - bool depth; - - /* Set if stencil buffers should be attached. - * TODO: Obsolete. */ - bool stencil; - - /* If depth and stencil are true, a packed 24/8 buffer will be added. - * Only attaching stencil is invalid and will be ignored. */ - - /* Use conventional bottom-left origin convention. If false, - * standard libretro top-left origin semantics are used. - * TODO: Move to GL specific interface. */ - bool bottom_left_origin; - - /* Major version number for core GL context or GLES 3.1+. */ - unsigned version_major; - - /* Minor version number for core GL context or GLES 3.1+. */ - unsigned version_minor; - - /* If this is true, the frontend will go very far to avoid - * resetting context in scenarios like toggling fullscreen, etc. - * TODO: Obsolete? Maybe frontend should just always assume this ... - */ - bool cache_context; - - /* The reset callback might still be called in extreme situations - * such as if the context is lost beyond recovery. - * - * For optimal stability, set this to false, and allow context to be - * reset at any time. - */ - - /* A callback to be called before the context is destroyed in a - * controlled way by the frontend. */ - retro_hw_context_reset_t context_destroy; - - /* OpenGL resources can be deinitialized cleanly at this step. - * context_destroy can be set to NULL, in which resources will - * just be destroyed without any notification. - * - * Even when context_destroy is non-NULL, it is possible that - * context_reset is called without any destroy notification. - * This happens if context is lost by external factors (such as - * notified by GL_ARB_robustness). - * - * In this case, the context is assumed to be already dead, - * and the libretro implementation must not try to free any OpenGL - * resources in the subsequent context_reset. - */ - - /* Creates a debug context. */ - bool debug_context; -}; - -/* Callback type passed in RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. - * Called by the frontend in response to keyboard events. - * down is set if the key is being pressed, or false if it is being released. - * keycode is the RETROK value of the char. - * character is the text character of the pressed key. (UTF-32). - * key_modifiers is a set of RETROKMOD values or'ed together. - * - * The pressed/keycode state can be indepedent of the character. - * It is also possible that multiple characters are generated from a - * single keypress. - * Keycode events should be treated separately from character events. - * However, when possible, the frontend should try to synchronize these. - * If only a character is posted, keycode should be RETROK_UNKNOWN. - * - * Similarily if only a keycode event is generated with no corresponding - * character, character should be 0. - */ -typedef void (RETRO_CALLCONV *retro_keyboard_event_t)(bool down, unsigned keycode, - uint32_t character, uint16_t key_modifiers); - -struct retro_keyboard_callback -{ - retro_keyboard_event_t callback; -}; - -/* Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE & - * RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE. - * Should be set for implementations which can swap out multiple disk - * images in runtime. - * - * If the implementation can do this automatically, it should strive to do so. - * However, there are cases where the user must manually do so. - * - * Overview: To swap a disk image, eject the disk image with - * set_eject_state(true). - * Set the disk index with set_image_index(index). Insert the disk again - * with set_eject_state(false). - */ - -/* If ejected is true, "ejects" the virtual disk tray. - * When ejected, the disk image index can be set. - */ -typedef bool (RETRO_CALLCONV *retro_set_eject_state_t)(bool ejected); - -/* Gets current eject state. The initial state is 'not ejected'. */ -typedef bool (RETRO_CALLCONV *retro_get_eject_state_t)(void); - -/* Gets current disk index. First disk is index 0. - * If return value is >= get_num_images(), no disk is currently inserted. - */ -typedef unsigned (RETRO_CALLCONV *retro_get_image_index_t)(void); - -/* Sets image index. Can only be called when disk is ejected. - * The implementation supports setting "no disk" by using an - * index >= get_num_images(). - */ -typedef bool (RETRO_CALLCONV *retro_set_image_index_t)(unsigned index); - -/* Gets total number of images which are available to use. */ -typedef unsigned (RETRO_CALLCONV *retro_get_num_images_t)(void); - -struct retro_game_info; - -/* Replaces the disk image associated with index. - * Arguments to pass in info have same requirements as retro_load_game(). - * Virtual disk tray must be ejected when calling this. - * - * Replacing a disk image with info = NULL will remove the disk image - * from the internal list. - * As a result, calls to get_image_index() can change. - * - * E.g. replace_image_index(1, NULL), and previous get_image_index() - * returned 4 before. - * Index 1 will be removed, and the new index is 3. - */ -typedef bool (RETRO_CALLCONV *retro_replace_image_index_t)(unsigned index, - const struct retro_game_info *info); - -/* Adds a new valid index (get_num_images()) to the internal disk list. - * This will increment subsequent return values from get_num_images() by 1. - * This image index cannot be used until a disk image has been set - * with replace_image_index. */ -typedef bool (RETRO_CALLCONV *retro_add_image_index_t)(void); - -/* Sets initial image to insert in drive when calling - * core_load_game(). - * Since we cannot pass the initial index when loading - * content (this would require a major API change), this - * is set by the frontend *before* calling the core's - * retro_load_game()/retro_load_game_special() implementation. - * A core should therefore cache the index/path values and handle - * them inside retro_load_game()/retro_load_game_special(). - * - If 'index' is invalid (index >= get_num_images()), the - * core should ignore the set value and instead use 0 - * - 'path' is used purely for error checking - i.e. when - * content is loaded, the core should verify that the - * disk specified by 'index' has the specified file path. - * This is to guard against auto selecting the wrong image - * if (for example) the user should modify an existing M3U - * playlist. We have to let the core handle this because - * set_initial_image() must be called before loading content, - * i.e. the frontend cannot access image paths in advance - * and thus cannot perform the error check itself. - * If set path and content path do not match, the core should - * ignore the set 'index' value and instead use 0 - * Returns 'false' if index or 'path' are invalid, or core - * does not support this functionality - */ -typedef bool (RETRO_CALLCONV *retro_set_initial_image_t)(unsigned index, const char *path); - -/* Fetches the path of the specified disk image file. - * Returns 'false' if index is invalid (index >= get_num_images()) - * or path is otherwise unavailable. - */ -typedef bool (RETRO_CALLCONV *retro_get_image_path_t)(unsigned index, char *path, size_t len); - -/* Fetches a core-provided 'label' for the specified disk - * image file. In the simplest case this may be a file name - * (without extension), but for cores with more complex - * content requirements information may be provided to - * facilitate user disk swapping - for example, a core - * running floppy-disk-based content may uniquely label - * save disks, data disks, level disks, etc. with names - * corresponding to in-game disk change prompts (so the - * frontend can provide better user guidance than a 'dumb' - * disk index value). - * Returns 'false' if index is invalid (index >= get_num_images()) - * or label is otherwise unavailable. - */ -typedef bool (RETRO_CALLCONV *retro_get_image_label_t)(unsigned index, char *label, size_t len); - -struct retro_disk_control_callback -{ - retro_set_eject_state_t set_eject_state; - retro_get_eject_state_t get_eject_state; - - retro_get_image_index_t get_image_index; - retro_set_image_index_t set_image_index; - retro_get_num_images_t get_num_images; - - retro_replace_image_index_t replace_image_index; - retro_add_image_index_t add_image_index; -}; - -struct retro_disk_control_ext_callback -{ - retro_set_eject_state_t set_eject_state; - retro_get_eject_state_t get_eject_state; - - retro_get_image_index_t get_image_index; - retro_set_image_index_t set_image_index; - retro_get_num_images_t get_num_images; - - retro_replace_image_index_t replace_image_index; - retro_add_image_index_t add_image_index; - - /* NOTE: Frontend will only attempt to record/restore - * last used disk index if both set_initial_image() - * and get_image_path() are implemented */ - retro_set_initial_image_t set_initial_image; /* Optional - may be NULL */ - - retro_get_image_path_t get_image_path; /* Optional - may be NULL */ - retro_get_image_label_t get_image_label; /* Optional - may be NULL */ -}; - -enum retro_pixel_format -{ - /* 0RGB1555, native endian. - * 0 bit must be set to 0. - * This pixel format is default for compatibility concerns only. - * If a 15/16-bit pixel format is desired, consider using RGB565. */ - RETRO_PIXEL_FORMAT_0RGB1555 = 0, - - /* XRGB8888, native endian. - * X bits are ignored. */ - RETRO_PIXEL_FORMAT_XRGB8888 = 1, - - /* RGB565, native endian. - * This pixel format is the recommended format to use if a 15/16-bit - * format is desired as it is the pixel format that is typically - * available on a wide range of low-power devices. - * - * It is also natively supported in APIs like OpenGL ES. */ - RETRO_PIXEL_FORMAT_RGB565 = 2, - - /* Ensure sizeof() == sizeof(int). */ - RETRO_PIXEL_FORMAT_UNKNOWN = INT_MAX -}; - -enum retro_savestate_context -{ - /* Standard savestate written to disk. */ - RETRO_SAVESTATE_CONTEXT_NORMAL = 0, - - /* Savestate where you are guaranteed that the same instance will load the save state. - * You can store internal pointers to code or data. - * It's still a full serialization and deserialization, and could be loaded or saved at any time. - * It won't be written to disk or sent over the network. - */ - RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE = 1, - - /* Savestate where you are guaranteed that the same emulator binary will load that savestate. - * You can skip anything that would slow down saving or loading state but you can not store internal pointers. - * It won't be written to disk or sent over the network. - * Example: "Second Instance" runahead - */ - RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_BINARY = 2, - - /* Savestate used within a rollback netplay feature. - * You should skip anything that would unnecessarily increase bandwidth usage. - * It won't be written to disk but it will be sent over the network. - */ - RETRO_SAVESTATE_CONTEXT_ROLLBACK_NETPLAY = 3, - - /* Ensure sizeof() == sizeof(int). */ - RETRO_SAVESTATE_CONTEXT_UNKNOWN = INT_MAX -}; - -struct retro_message -{ - const char *msg; /* Message to be displayed. */ - unsigned frames; /* Duration in frames of message. */ -}; - -enum retro_message_target -{ - RETRO_MESSAGE_TARGET_ALL = 0, - RETRO_MESSAGE_TARGET_OSD, - RETRO_MESSAGE_TARGET_LOG -}; - -enum retro_message_type -{ - RETRO_MESSAGE_TYPE_NOTIFICATION = 0, - RETRO_MESSAGE_TYPE_NOTIFICATION_ALT, - RETRO_MESSAGE_TYPE_STATUS, - RETRO_MESSAGE_TYPE_PROGRESS -}; - -struct retro_message_ext -{ - /* Message string to be displayed/logged */ - const char *msg; - /* Duration (in ms) of message when targeting the OSD */ - unsigned duration; - /* Message priority when targeting the OSD - * > When multiple concurrent messages are sent to - * the frontend and the frontend does not have the - * capacity to display them all, messages with the - * *highest* priority value should be shown - * > There is no upper limit to a message priority - * value (within the bounds of the unsigned data type) - * > In the reference frontend (RetroArch), the same - * priority values are used for frontend-generated - * notifications, which are typically assigned values - * between 0 and 3 depending upon importance */ - unsigned priority; - /* Message logging level (info, warn, error, etc.) */ - enum retro_log_level level; - /* Message destination: OSD, logging interface or both */ - enum retro_message_target target; - /* Message 'type' when targeting the OSD - * > RETRO_MESSAGE_TYPE_NOTIFICATION: Specifies that a - * message should be handled in identical fashion to - * a standard frontend-generated notification - * > RETRO_MESSAGE_TYPE_NOTIFICATION_ALT: Specifies that - * message is a notification that requires user attention - * or action, but that it should be displayed in a manner - * that differs from standard frontend-generated notifications. - * This would typically correspond to messages that should be - * displayed immediately (independently from any internal - * frontend message queue), and/or which should be visually - * distinguishable from frontend-generated notifications. - * For example, a core may wish to inform the user of - * information related to a disk-change event. It is - * expected that the frontend itself may provide a - * notification in this case; if the core sends a - * message of type RETRO_MESSAGE_TYPE_NOTIFICATION, an - * uncomfortable 'double-notification' may occur. A message - * of RETRO_MESSAGE_TYPE_NOTIFICATION_ALT should therefore - * be presented such that visual conflict with regular - * notifications does not occur - * > RETRO_MESSAGE_TYPE_STATUS: Indicates that message - * is not a standard notification. This typically - * corresponds to 'status' indicators, such as a core's - * internal FPS, which are intended to be displayed - * either permanently while a core is running, or in - * a manner that does not suggest user attention or action - * is required. 'Status' type messages should therefore be - * displayed in a different on-screen location and in a manner - * easily distinguishable from both standard frontend-generated - * notifications and messages of type RETRO_MESSAGE_TYPE_NOTIFICATION_ALT - * > RETRO_MESSAGE_TYPE_PROGRESS: Indicates that message reports - * the progress of an internal core task. For example, in cases - * where a core itself handles the loading of content from a file, - * this may correspond to the percentage of the file that has been - * read. Alternatively, an audio/video playback core may use a - * message of type RETRO_MESSAGE_TYPE_PROGRESS to display the current - * playback position as a percentage of the runtime. 'Progress' type - * messages should therefore be displayed as a literal progress bar, - * where: - * - 'retro_message_ext.msg' is the progress bar title/label - * - 'retro_message_ext.progress' determines the length of - * the progress bar - * NOTE: Message type is a *hint*, and may be ignored - * by the frontend. If a frontend lacks support for - * displaying messages via alternate means than standard - * frontend-generated notifications, it will treat *all* - * messages as having the type RETRO_MESSAGE_TYPE_NOTIFICATION */ - enum retro_message_type type; - /* Task progress when targeting the OSD and message is - * of type RETRO_MESSAGE_TYPE_PROGRESS - * > -1: Unmetered/indeterminate - * > 0-100: Current progress percentage - * NOTE: Since message type is a hint, a frontend may ignore - * progress values. Where relevant, a core should therefore - * include progress percentage within the message string, - * such that the message intent remains clear when displayed - * as a standard frontend-generated notification */ - int8_t progress; -}; - -/* Describes how the libretro implementation maps a libretro input bind - * to its internal input system through a human readable string. - * This string can be used to better let a user configure input. */ -struct retro_input_descriptor -{ - /* Associates given parameters with a description. */ - unsigned port; - unsigned device; - unsigned index; - unsigned id; - - /* Human readable description for parameters. - * The pointer must remain valid until - * retro_unload_game() is called. */ - const char *description; -}; - -struct retro_system_info -{ - /* All pointers are owned by libretro implementation, and pointers must - * remain valid until it is unloaded. */ - - const char *library_name; /* Descriptive name of library. Should not - * contain any version numbers, etc. */ - const char *library_version; /* Descriptive version of core. */ - - const char *valid_extensions; /* A string listing probably content - * extensions the core will be able to - * load, separated with pipe. - * I.e. "bin|rom|iso". - * Typically used for a GUI to filter - * out extensions. */ - - /* Libretro cores that need to have direct access to their content - * files, including cores which use the path of the content files to - * determine the paths of other files, should set need_fullpath to true. - * - * Cores should strive for setting need_fullpath to false, - * as it allows the frontend to perform patching, etc. - * - * If need_fullpath is true and retro_load_game() is called: - * - retro_game_info::path is guaranteed to have a valid path - * - retro_game_info::data and retro_game_info::size are invalid - * - * If need_fullpath is false and retro_load_game() is called: - * - retro_game_info::path may be NULL - * - retro_game_info::data and retro_game_info::size are guaranteed - * to be valid - * - * See also: - * - RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY - * - RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY - */ - bool need_fullpath; - - /* If true, the frontend is not allowed to extract any archives before - * loading the real content. - * Necessary for certain libretro implementations that load games - * from zipped archives. */ - bool block_extract; -}; - -/* Defines overrides which modify frontend handling of - * specific content file types. - * An array of retro_system_content_info_override is - * passed to RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE - * NOTE: In the following descriptions, references to - * retro_load_game() may be replaced with - * retro_load_game_special() */ -struct retro_system_content_info_override -{ - /* A list of file extensions for which the override - * should apply, delimited by a 'pipe' character - * (e.g. "md|sms|gg") - * Permitted file extensions are limited to those - * included in retro_system_info::valid_extensions - * and/or retro_subsystem_rom_info::valid_extensions */ - const char *extensions; - - /* Overrides the need_fullpath value set in - * retro_system_info and/or retro_subsystem_rom_info. - * To reiterate: - * - * If need_fullpath is true and retro_load_game() is called: - * - retro_game_info::path is guaranteed to contain a valid - * path to an existent file - * - retro_game_info::data and retro_game_info::size are invalid - * - * If need_fullpath is false and retro_load_game() is called: - * - retro_game_info::path may be NULL - * - retro_game_info::data and retro_game_info::size are guaranteed - * to be valid - * - * In addition: - * - * If need_fullpath is true and retro_load_game() is called: - * - retro_game_info_ext::full_path is guaranteed to contain a valid - * path to an existent file - * - retro_game_info_ext::archive_path may be NULL - * - retro_game_info_ext::archive_file may be NULL - * - retro_game_info_ext::dir is guaranteed to contain a valid path - * to the directory in which the content file exists - * - retro_game_info_ext::name is guaranteed to contain the - * basename of the content file, without extension - * - retro_game_info_ext::ext is guaranteed to contain the - * extension of the content file in lower case format - * - retro_game_info_ext::data and retro_game_info_ext::size - * are invalid - * - * If need_fullpath is false and retro_load_game() is called: - * - If retro_game_info_ext::file_in_archive is false: - * - retro_game_info_ext::full_path is guaranteed to contain - * a valid path to an existent file - * - retro_game_info_ext::archive_path may be NULL - * - retro_game_info_ext::archive_file may be NULL - * - retro_game_info_ext::dir is guaranteed to contain a - * valid path to the directory in which the content file exists - * - retro_game_info_ext::name is guaranteed to contain the - * basename of the content file, without extension - * - retro_game_info_ext::ext is guaranteed to contain the - * extension of the content file in lower case format - * - If retro_game_info_ext::file_in_archive is true: - * - retro_game_info_ext::full_path may be NULL - * - retro_game_info_ext::archive_path is guaranteed to - * contain a valid path to an existent compressed file - * inside which the content file is located - * - retro_game_info_ext::archive_file is guaranteed to - * contain a valid path to an existent content file - * inside the compressed file referred to by - * retro_game_info_ext::archive_path - * e.g. for a compressed file '/path/to/foo.zip' - * containing 'bar.sfc' - * > retro_game_info_ext::archive_path will be '/path/to/foo.zip' - * > retro_game_info_ext::archive_file will be 'bar.sfc' - * - retro_game_info_ext::dir is guaranteed to contain a - * valid path to the directory in which the compressed file - * (containing the content file) exists - * - retro_game_info_ext::name is guaranteed to contain - * EITHER - * 1) the basename of the compressed file (containing - * the content file), without extension - * OR - * 2) the basename of the content file inside the - * compressed file, without extension - * In either case, a core should consider 'name' to - * be the canonical name/ID of the the content file - * - retro_game_info_ext::ext is guaranteed to contain the - * extension of the content file inside the compressed file, - * in lower case format - * - retro_game_info_ext::data and retro_game_info_ext::size are - * guaranteed to be valid */ - bool need_fullpath; - - /* If need_fullpath is false, specifies whether the content - * data buffer available in retro_load_game() is 'persistent' - * - * If persistent_data is false and retro_load_game() is called: - * - retro_game_info::data and retro_game_info::size - * are valid only until retro_load_game() returns - * - retro_game_info_ext::data and retro_game_info_ext::size - * are valid only until retro_load_game() returns - * - * If persistent_data is true and retro_load_game() is called: - * - retro_game_info::data and retro_game_info::size - * are valid until retro_deinit() returns - * - retro_game_info_ext::data and retro_game_info_ext::size - * are valid until retro_deinit() returns */ - bool persistent_data; -}; - -/* Similar to retro_game_info, but provides extended - * information about the source content file and - * game memory buffer status. - * And array of retro_game_info_ext is returned by - * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT - * NOTE: In the following descriptions, references to - * retro_load_game() may be replaced with - * retro_load_game_special() */ -struct retro_game_info_ext -{ - /* - If file_in_archive is false, contains a valid - * path to an existent content file (UTF-8 encoded) - * - If file_in_archive is true, may be NULL */ - const char *full_path; - - /* - If file_in_archive is false, may be NULL - * - If file_in_archive is true, contains a valid path - * to an existent compressed file inside which the - * content file is located (UTF-8 encoded) */ - const char *archive_path; - - /* - If file_in_archive is false, may be NULL - * - If file_in_archive is true, contain a valid path - * to an existent content file inside the compressed - * file referred to by archive_path (UTF-8 encoded) - * e.g. for a compressed file '/path/to/foo.zip' - * containing 'bar.sfc' - * > archive_path will be '/path/to/foo.zip' - * > archive_file will be 'bar.sfc' */ - const char *archive_file; - - /* - If file_in_archive is false, contains a valid path - * to the directory in which the content file exists - * (UTF-8 encoded) - * - If file_in_archive is true, contains a valid path - * to the directory in which the compressed file - * (containing the content file) exists (UTF-8 encoded) */ - const char *dir; - - /* Contains the canonical name/ID of the content file - * (UTF-8 encoded). Intended for use when identifying - * 'complementary' content named after the loaded file - - * i.e. companion data of a different format (a CD image - * required by a ROM), texture packs, internally handled - * save files, etc. - * - If file_in_archive is false, contains the basename - * of the content file, without extension - * - If file_in_archive is true, then string is - * implementation specific. A frontend may choose to - * set a name value of: - * EITHER - * 1) the basename of the compressed file (containing - * the content file), without extension - * OR - * 2) the basename of the content file inside the - * compressed file, without extension - * RetroArch sets the 'name' value according to (1). - * A frontend that supports routine loading of - * content from archives containing multiple unrelated - * content files may set the 'name' value according - * to (2). */ - const char *name; - - /* - If file_in_archive is false, contains the extension - * of the content file in lower case format - * - If file_in_archive is true, contains the extension - * of the content file inside the compressed file, - * in lower case format */ - const char *ext; - - /* String of implementation specific meta-data. */ - const char *meta; - - /* Memory buffer of loaded game content. Will be NULL: - * IF - * - retro_system_info::need_fullpath is true and - * retro_system_content_info_override::need_fullpath - * is unset - * OR - * - retro_system_content_info_override::need_fullpath - * is true */ - const void *data; - - /* Size of game content memory buffer, in bytes */ - size_t size; - - /* True if loaded content file is inside a compressed - * archive */ - bool file_in_archive; - - /* - If data is NULL, value is unset/ignored - * - If data is non-NULL: - * - If persistent_data is false, data and size are - * valid only until retro_load_game() returns - * - If persistent_data is true, data and size are - * are valid until retro_deinit() returns */ - bool persistent_data; -}; - -struct retro_game_geometry -{ - unsigned base_width; /* Nominal video width of game. */ - unsigned base_height; /* Nominal video height of game. */ - unsigned max_width; /* Maximum possible width of game. */ - unsigned max_height; /* Maximum possible height of game. */ - - float aspect_ratio; /* Nominal aspect ratio of game. If - * aspect_ratio is <= 0.0, an aspect ratio - * of base_width / base_height is assumed. - * A frontend could override this setting, - * if desired. */ -}; - -struct retro_system_timing -{ - double fps; /* FPS of video content. */ - double sample_rate; /* Sampling rate of audio. */ -}; - -struct retro_system_av_info -{ - struct retro_game_geometry geometry; - struct retro_system_timing timing; -}; - -struct retro_variable -{ - /* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. - * If NULL, obtains the complete environment string if more - * complex parsing is necessary. - * The environment string is formatted as key-value pairs - * delimited by semicolons as so: - * "key1=value1;key2=value2;..." - */ - const char *key; - - /* Value to be obtained. If key does not exist, it is set to NULL. */ - const char *value; -}; - -struct retro_core_option_display -{ - /* Variable to configure in RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY */ - const char *key; - - /* Specifies whether variable should be displayed - * when presenting core options to the user */ - bool visible; -}; - -/* Maximum number of values permitted for a core option - * > Note: We have to set a maximum value due the limitations - * of the C language - i.e. it is not possible to create an - * array of structs each containing a variable sized array, - * so the retro_core_option_definition values array must - * have a fixed size. The size limit of 128 is a balancing - * act - it needs to be large enough to support all 'sane' - * core options, but setting it too large may impact low memory - * platforms. In practise, if a core option has more than - * 128 values then the implementation is likely flawed. - * To quote the above API reference: - * "The number of possible options should be very limited - * i.e. it should be feasible to cycle through options - * without a keyboard." - */ -#define RETRO_NUM_CORE_OPTION_VALUES_MAX 128 - -struct retro_core_option_value -{ - /* Expected option value */ - const char *value; - - /* Human-readable value label. If NULL, value itself - * will be displayed by the frontend */ - const char *label; -}; - -struct retro_core_option_definition -{ - /* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. */ - const char *key; - - /* Human-readable core option description (used as menu label) */ - const char *desc; - - /* Human-readable core option information (used as menu sublabel) */ - const char *info; - - /* Array of retro_core_option_value structs, terminated by NULL */ - struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX]; - - /* Default core option value. Must match one of the values - * in the retro_core_option_value array, otherwise will be - * ignored */ - const char *default_value; -}; - -#ifdef __PS3__ -#undef local -#endif - -struct retro_core_options_intl -{ - /* Pointer to an array of retro_core_option_definition structs - * - US English implementation - * - Must point to a valid array */ - struct retro_core_option_definition *us; - - /* Pointer to an array of retro_core_option_definition structs - * - Implementation for current frontend language - * - May be NULL */ - struct retro_core_option_definition *local; -}; - -struct retro_core_option_v2_category -{ - /* Variable uniquely identifying the - * option category. Valid key characters - * are [a-z, A-Z, 0-9, _, -] */ - const char *key; - - /* Human-readable category description - * > Used as category menu label when - * frontend has core option category - * support */ - const char *desc; - - /* Human-readable category information - * > Used as category menu sublabel when - * frontend has core option category - * support - * > Optional (may be NULL or an empty - * string) */ - const char *info; -}; - -struct retro_core_option_v2_definition -{ - /* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. - * Valid key characters are [a-z, A-Z, 0-9, _, -] */ - const char *key; - - /* Human-readable core option description - * > Used as menu label when frontend does - * not have core option category support - * e.g. "Video > Aspect Ratio" */ - const char *desc; - - /* Human-readable core option description - * > Used as menu label when frontend has - * core option category support - * e.g. "Aspect Ratio", where associated - * retro_core_option_v2_category::desc - * is "Video" - * > If empty or NULL, the string specified by - * desc will be used as the menu label - * > Will be ignored (and may be set to NULL) - * if category_key is empty or NULL */ - const char *desc_categorized; - - /* Human-readable core option information - * > Used as menu sublabel */ - const char *info; - - /* Human-readable core option information - * > Used as menu sublabel when frontend - * has core option category support - * (e.g. may be required when info text - * references an option by name/desc, - * and the desc/desc_categorized text - * for that option differ) - * > If empty or NULL, the string specified by - * info will be used as the menu sublabel - * > Will be ignored (and may be set to NULL) - * if category_key is empty or NULL */ - const char *info_categorized; - - /* Variable specifying category (e.g. "video", - * "audio") that will be assigned to the option - * if frontend has core option category support. - * > Categorized options will be displayed in a - * subsection/submenu of the frontend core - * option interface - * > Specified string must match one of the - * retro_core_option_v2_category::key values - * in the associated retro_core_option_v2_category - * array; If no match is not found, specified - * string will be considered as NULL - * > If specified string is empty or NULL, option will - * have no category and will be shown at the top - * level of the frontend core option interface */ - const char *category_key; - - /* Array of retro_core_option_value structs, terminated by NULL */ - struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX]; - - /* Default core option value. Must match one of the values - * in the retro_core_option_value array, otherwise will be - * ignored */ - const char *default_value; -}; - -struct retro_core_options_v2 -{ - /* Array of retro_core_option_v2_category structs, - * terminated by NULL - * > If NULL, all entries in definitions array - * will have no category and will be shown at - * the top level of the frontend core option - * interface - * > Will be ignored if frontend does not have - * core option category support */ - struct retro_core_option_v2_category *categories; - - /* Array of retro_core_option_v2_definition structs, - * terminated by NULL */ - struct retro_core_option_v2_definition *definitions; -}; - -struct retro_core_options_v2_intl -{ - /* Pointer to a retro_core_options_v2 struct - * > US English implementation - * > Must point to a valid struct */ - struct retro_core_options_v2 *us; - - /* Pointer to a retro_core_options_v2 struct - * - Implementation for current frontend language - * - May be NULL */ - struct retro_core_options_v2 *local; -}; - -/* Used by the frontend to monitor changes in core option - * visibility. May be called each time any core option - * value is set via the frontend. - * - On each invocation, the core must update the visibility - * of any dynamically hidden options using the - * RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY environment - * callback. - * - On the first invocation, returns 'true' if the visibility - * of any core option has changed since the last call of - * retro_load_game() or retro_load_game_special(). - * - On each subsequent invocation, returns 'true' if the - * visibility of any core option has changed since the last - * time the function was called. */ -typedef bool (RETRO_CALLCONV *retro_core_options_update_display_callback_t)(void); -struct retro_core_options_update_display_callback -{ - retro_core_options_update_display_callback_t callback; -}; - -struct retro_game_info -{ - const char *path; /* Path to game, UTF-8 encoded. - * Sometimes used as a reference for building other paths. - * May be NULL if game was loaded from stdin or similar, - * but in this case some cores will be unable to load `data`. - * So, it is preferable to fabricate something here instead - * of passing NULL, which will help more cores to succeed. - * retro_system_info::need_fullpath requires - * that this path is valid. */ - const void *data; /* Memory buffer of loaded game. Will be NULL - * if need_fullpath was set. */ - size_t size; /* Size of memory buffer. */ - const char *meta; /* String of implementation specific meta-data. */ -}; - -#define RETRO_MEMORY_ACCESS_WRITE (1 << 0) - /* The core will write to the buffer provided by retro_framebuffer::data. */ -#define RETRO_MEMORY_ACCESS_READ (1 << 1) - /* The core will read from retro_framebuffer::data. */ -#define RETRO_MEMORY_TYPE_CACHED (1 << 0) - /* The memory in data is cached. - * If not cached, random writes and/or reading from the buffer is expected to be very slow. */ -struct retro_framebuffer -{ - void *data; /* The framebuffer which the core can render into. - Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. - The initial contents of data are unspecified. */ - unsigned width; /* The framebuffer width used by the core. Set by core. */ - unsigned height; /* The framebuffer height used by the core. Set by core. */ - size_t pitch; /* The number of bytes between the beginning of a scanline, - and beginning of the next scanline. - Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */ - enum retro_pixel_format format; /* The pixel format the core must use to render into data. - This format could differ from the format used in - SET_PIXEL_FORMAT. - Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */ - - unsigned access_flags; /* How the core will access the memory in the framebuffer. - RETRO_MEMORY_ACCESS_* flags. - Set by core. */ - unsigned memory_flags; /* Flags telling core how the memory has been mapped. - RETRO_MEMORY_TYPE_* flags. - Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */ -}; - -/* Used by a libretro core to override the current - * fastforwarding mode of the frontend */ -struct retro_fastforwarding_override -{ - /* Specifies the runtime speed multiplier that - * will be applied when 'fastforward' is true. - * For example, a value of 5.0 when running 60 FPS - * content will cap the fast-forward rate at 300 FPS. - * Note that the target multiplier may not be achieved - * if the host hardware has insufficient processing - * power. - * Setting a value of 0.0 (or greater than 0.0 but - * less than 1.0) will result in an uncapped - * fast-forward rate (limited only by hardware - * capacity). - * If the value is negative, it will be ignored - * (i.e. the frontend will use a runtime speed - * multiplier of its own choosing) */ - float ratio; - - /* If true, fastforwarding mode will be enabled. - * If false, fastforwarding mode will be disabled. */ - bool fastforward; - - /* If true, and if supported by the frontend, an - * on-screen notification will be displayed while - * 'fastforward' is true. - * If false, and if supported by the frontend, any - * on-screen fast-forward notifications will be - * suppressed */ - bool notification; - - /* If true, the core will have sole control over - * when fastforwarding mode is enabled/disabled; - * the frontend will not be able to change the - * state set by 'fastforward' until either - * 'inhibit_toggle' is set to false, or the core - * is unloaded */ - bool inhibit_toggle; -}; - -/* During normal operation. Rate will be equal to the core's internal FPS. */ -#define RETRO_THROTTLE_NONE 0 - -/* While paused or stepping single frames. Rate will be 0. */ -#define RETRO_THROTTLE_FRAME_STEPPING 1 - -/* During fast forwarding. - * Rate will be 0 if not specifically limited to a maximum speed. */ -#define RETRO_THROTTLE_FAST_FORWARD 2 - -/* During slow motion. Rate will be less than the core's internal FPS. */ -#define RETRO_THROTTLE_SLOW_MOTION 3 - -/* While rewinding recorded save states. Rate can vary depending on the rewind - * speed or be 0 if the frontend is not aiming for a specific rate. */ -#define RETRO_THROTTLE_REWINDING 4 - -/* While vsync is active in the video driver and the target refresh rate is - * lower than the core's internal FPS. Rate is the target refresh rate. */ -#define RETRO_THROTTLE_VSYNC 5 - -/* When the frontend does not throttle in any way. Rate will be 0. - * An example could be if no vsync or audio output is active. */ -#define RETRO_THROTTLE_UNBLOCKED 6 - -struct retro_throttle_state -{ - /* The current throttling mode. Should be one of the values above. */ - unsigned mode; - - /* How many times per second the frontend aims to call retro_run. - * Depending on the mode, it can be 0 if there is no known fixed rate. - * This won't be accurate if the total processing time of the core and - * the frontend is longer than what is available for one frame. */ - float rate; -}; - -/* Callbacks */ - -/* Environment callback. Gives implementations a way of performing - * uncommon tasks. Extensible. */ -typedef bool (RETRO_CALLCONV *retro_environment_t)(unsigned cmd, void *data); - -/* Render a frame. Pixel format is 15-bit 0RGB1555 native endian - * unless changed (see RETRO_ENVIRONMENT_SET_PIXEL_FORMAT). - * - * Width and height specify dimensions of buffer. - * Pitch specifices length in bytes between two lines in buffer. - * - * For performance reasons, it is highly recommended to have a frame - * that is packed in memory, i.e. pitch == width * byte_per_pixel. - * Certain graphic APIs, such as OpenGL ES, do not like textures - * that are not packed in memory. - */ -typedef void (RETRO_CALLCONV *retro_video_refresh_t)(const void *data, unsigned width, - unsigned height, size_t pitch); - -/* Renders a single audio frame. Should only be used if implementation - * generates a single sample at a time. - * Format is signed 16-bit native endian. - */ -typedef void (RETRO_CALLCONV *retro_audio_sample_t)(int16_t left, int16_t right); - -/* Renders multiple audio frames in one go. - * - * One frame is defined as a sample of left and right channels, interleaved. - * I.e. int16_t buf[4] = { l, r, l, r }; would be 2 frames. - * Only one of the audio callbacks must ever be used. - */ -typedef size_t (RETRO_CALLCONV *retro_audio_sample_batch_t)(const int16_t *data, - size_t frames); - -/* Polls input. */ -typedef void (RETRO_CALLCONV *retro_input_poll_t)(void); - -/* Queries for input for player 'port'. device will be masked with - * RETRO_DEVICE_MASK. - * - * Specialization of devices such as RETRO_DEVICE_JOYPAD_MULTITAP that - * have been set with retro_set_controller_port_device() - * will still use the higher level RETRO_DEVICE_JOYPAD to request input. - */ -typedef int16_t (RETRO_CALLCONV *retro_input_state_t)(unsigned port, unsigned device, - unsigned index, unsigned id); - -/* Sets callbacks. retro_set_environment() is guaranteed to be called - * before retro_init(). - * - * The rest of the set_* functions are guaranteed to have been called - * before the first call to retro_run() is made. */ -RETRO_API void retro_set_environment(retro_environment_t); -RETRO_API void retro_set_video_refresh(retro_video_refresh_t); -RETRO_API void retro_set_audio_sample(retro_audio_sample_t); -RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t); -RETRO_API void retro_set_input_poll(retro_input_poll_t); -RETRO_API void retro_set_input_state(retro_input_state_t); - -/* Library global initialization/deinitialization. */ -RETRO_API void retro_init(void); -RETRO_API void retro_deinit(void); - -/* Must return RETRO_API_VERSION. Used to validate ABI compatibility - * when the API is revised. */ -RETRO_API unsigned retro_api_version(void); - -/* Gets statically known system info. Pointers provided in *info - * must be statically allocated. - * Can be called at any time, even before retro_init(). */ -RETRO_API void retro_get_system_info(struct retro_system_info *info); - -/* Gets information about system audio/video timings and geometry. - * Can be called only after retro_load_game() has successfully completed. - * NOTE: The implementation of this function might not initialize every - * variable if needed. - * E.g. geom.aspect_ratio might not be initialized if core doesn't - * desire a particular aspect ratio. */ -RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info); - -/* Sets device to be used for player 'port'. - * By default, RETRO_DEVICE_JOYPAD is assumed to be plugged into all - * available ports. - * Setting a particular device type is not a guarantee that libretro cores - * will only poll input based on that particular device type. It is only a - * hint to the libretro core when a core cannot automatically detect the - * appropriate input device type on its own. It is also relevant when a - * core can change its behavior depending on device type. - * - * As part of the core's implementation of retro_set_controller_port_device, - * the core should call RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS to notify the - * frontend if the descriptions for any controls have changed as a - * result of changing the device type. - */ -RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device); - -/* Resets the current game. */ -RETRO_API void retro_reset(void); - -/* Runs the game for one video frame. - * During retro_run(), input_poll callback must be called at least once. - * - * If a frame is not rendered for reasons where a game "dropped" a frame, - * this still counts as a frame, and retro_run() should explicitly dupe - * a frame if GET_CAN_DUPE returns true. - * In this case, the video callback can take a NULL argument for data. - */ -RETRO_API void retro_run(void); - -/* Returns the amount of data the implementation requires to serialize - * internal state (save states). - * Between calls to retro_load_game() and retro_unload_game(), the - * returned size is never allowed to be larger than a previous returned - * value, to ensure that the frontend can allocate a save state buffer once. - */ -RETRO_API size_t retro_serialize_size(void); - -/* Serializes internal state. If failed, or size is lower than - * retro_serialize_size(), it should return false, true otherwise. */ -RETRO_API bool retro_serialize(void *data, size_t size); -RETRO_API bool retro_unserialize(const void *data, size_t size); - -RETRO_API void retro_cheat_reset(void); -RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code); - -/* Loads a game. - * Return true to indicate successful loading and false to indicate load failure. - */ -RETRO_API bool retro_load_game(const struct retro_game_info *game); - -/* Loads a "special" kind of game. Should not be used, - * except in extreme cases. */ -RETRO_API bool retro_load_game_special( - unsigned game_type, - const struct retro_game_info *info, size_t num_info -); - -/* Unloads the currently loaded game. Called before retro_deinit(void). */ -RETRO_API void retro_unload_game(void); - -/* Gets region of game. */ -RETRO_API unsigned retro_get_region(void); - -/* Gets region of memory. */ -RETRO_API void *retro_get_memory_data(unsigned id); -RETRO_API size_t retro_get_memory_size(unsigned id); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libretro/libretro_core_options.h b/libretro/libretro_core_options.h index 7dbbb664bf..a8a1bd3707 100644 --- a/libretro/libretro_core_options.h +++ b/libretro/libretro_core_options.h @@ -4,8 +4,8 @@ #include #include -#include "libretro.h" -#include "retro_inline.h" +#include +#include #ifndef HAVE_NO_LANGEXTRA #include "libretro_core_options_intl.h" diff --git a/libretro/libretro_d3d.h b/libretro/libretro_d3d.h deleted file mode 100644 index 2ed71aa7c0..0000000000 --- a/libretro/libretro_d3d.h +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright (C) 2010-2016 The RetroArch team - * - * --------------------------------------------------------------------------------------------- - * The following license statement only applies to this libretro API header (libretro_vulkan.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_DIRECT3D_H__ -#define LIBRETRO_DIRECT3D_H__ - -#include "libretro.h" - -#ifdef HAVE_D3D11 - -#include -#include - -#define RETRO_HW_RENDER_INTERFACE_D3D11_VERSION 1 - -struct retro_hw_render_interface_d3d11 -{ - /* Must be set to RETRO_HW_RENDER_INTERFACE_D3D11. */ - enum retro_hw_render_interface_type interface_type; - /* Must be set to RETRO_HW_RENDER_INTERFACE_D3D11_VERSION. */ - unsigned interface_version; - - /* Opaque handle to the d3d11 backend in the frontend - * which must be passed along to all function pointers - * in this interface. - */ - void* handle; - ID3D11Device *device; - ID3D11DeviceContext *context; - D3D_FEATURE_LEVEL featureLevel; - pD3DCompile D3DCompile; -}; - - -#endif - -#endif /* LIBRETRO_DIRECT3D_H__ */ diff --git a/libretro/libretro_vulkan.cpp b/libretro/libretro_vulkan.cpp index f505ffd1ce..ddf5184611 100644 --- a/libretro/libretro_vulkan.cpp +++ b/libretro/libretro_vulkan.cpp @@ -9,7 +9,8 @@ #include "Core/Config.h" #define VK_NO_PROTOTYPES -#include "libretro/libretro_vulkan.h" +#include +#include "libretro/LibretroGraphicsContext.h" using namespace PPSSPP_VK; diff --git a/libretro/libretro_vulkan.h b/libretro/libretro_vulkan.h deleted file mode 100644 index 3f10da9f6c..0000000000 --- a/libretro/libretro_vulkan.h +++ /dev/null @@ -1,404 +0,0 @@ -/* Copyright (C) 2010-2016 The RetroArch team - * - * --------------------------------------------------------------------------------------------- - * The following license statement only applies to this libretro API header (libretro_vulkan.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_VULKAN_H__ -#define LIBRETRO_VULKAN_H__ - -#include "ext/vulkan/vulkan.h" -#include "libretro.h" -#include "LibretroGraphicsContext.h" - -#define RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION 5 -#define RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION 1 - -struct retro_vulkan_image -{ - VkImageView image_view; - VkImageLayout image_layout; - VkImageViewCreateInfo create_info; -}; - -typedef void (*retro_vulkan_set_image_t)(void* handle, const struct retro_vulkan_image* image, - uint32_t num_semaphores, const VkSemaphore* semaphores, - uint32_t src_queue_family); - -typedef uint32_t (*retro_vulkan_get_sync_index_t)(void* handle); -typedef uint32_t (*retro_vulkan_get_sync_index_mask_t)(void* handle); -typedef void (*retro_vulkan_set_command_buffers_t)(void* handle, uint32_t num_cmd, - const VkCommandBuffer* cmd); -typedef void (*retro_vulkan_wait_sync_index_t)(void* handle); -typedef void (*retro_vulkan_lock_queue_t)(void* handle); -typedef void (*retro_vulkan_unlock_queue_t)(void* handle); -typedef void (*retro_vulkan_set_signal_semaphore_t)(void* handle, VkSemaphore semaphore); - -typedef const VkApplicationInfo* (*retro_vulkan_get_application_info_t)(void); - -struct retro_vulkan_context -{ - VkPhysicalDevice gpu; - VkDevice device; - VkQueue queue; - uint32_t queue_family_index; - VkQueue presentation_queue; - uint32_t presentation_queue_family_index; -}; - -typedef bool (*retro_vulkan_create_device_t)( - struct retro_vulkan_context* context, VkInstance instance, VkPhysicalDevice gpu, - VkSurfaceKHR surface, PFN_vkGetInstanceProcAddr get_instance_proc_addr, - const char** required_device_extensions, unsigned num_required_device_extensions, - const char** required_device_layers, unsigned num_required_device_layers, - const VkPhysicalDeviceFeatures* required_features); - -typedef void (*retro_vulkan_destroy_device_t)(void); - -/* Note on thread safety: - * The Vulkan API is heavily designed around multi-threading, and - * the libretro interface for it should also be threading friendly. - * A core should be able to build command buffers and submit - * command buffers to the GPU from any thread. - */ - -struct retro_hw_render_context_negotiation_interface_vulkan -{ - /* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN. */ - enum retro_hw_render_context_negotiation_interface_type interface_type; - /* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION. */ - unsigned interface_version; - - /* If non-NULL, returns a VkApplicationInfo struct that the frontend can use instead of - * its "default" application info. - */ - retro_vulkan_get_application_info_t get_application_info; - - /* If non-NULL, the libretro core will choose one or more physical devices, - * create one or more logical devices and create one or more queues. - * The core must prepare a designated PhysicalDevice, Device, Queue and queue family index - * which the frontend will use for its internal operation. - * - * If gpu is not VK_NULL_HANDLE, the physical device provided to the frontend must be this - * PhysicalDevice. - * The core is still free to use other physical devices. - * - * The frontend will request certain extensions and layers for a device which is created. - * The core must ensure that the queue and queue_family_index support GRAPHICS and COMPUTE. - * - * If surface is not VK_NULL_HANDLE, the core must consider presentation when creating the queues. - * If presentation to "surface" is supported on the queue, presentation_queue must be equal to - * queue. - * If not, a second queue must be provided in presentation_queue and presentation_queue_index. - * If surface is not VK_NULL_HANDLE, the instance from frontend will have been created with - * supported for - * VK_KHR_surface extension. - * - * The core is free to set its own queue priorities. - * Device provided to frontend is owned by the frontend, but any additional device resources must - * be freed by core - * in destroy_device callback. - * - * If this function returns true, a PhysicalDevice, Device and Queues are initialized. - * If false, none of the above have been initialized and the frontend will attempt - * to fallback to "default" device creation, as if this function was never called. - */ - retro_vulkan_create_device_t create_device; - - /* If non-NULL, this callback is called similar to context_destroy for HW_RENDER_INTERFACE. - * However, it will be called even if context_reset was not called. - * This can happen if the context never succeeds in being created. - * destroy_device will always be called before the VkInstance - * of the frontend is destroyed if create_device was called successfully so that the core has a - * chance of - * tearing down its own device resources. - * - * Only auxillary resources should be freed here, i.e. resources which are not part of - * retro_vulkan_context. - */ - retro_vulkan_destroy_device_t destroy_device; -}; - -struct retro_hw_render_interface_vulkan -{ - /* Must be set to RETRO_HW_RENDER_INTERFACE_VULKAN. */ - enum retro_hw_render_interface_type interface_type; - /* Must be set to RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION. */ - unsigned interface_version; - - /* Opaque handle to the Vulkan backend in the frontend - * which must be passed along to all function pointers - * in this interface. - * - * The rationale for including a handle here (which libretro v1 - * doesn't currently do in general) is: - * - * - Vulkan cores should be able to be freely threaded without lots of fuzz. - * This would break frontends which currently rely on TLS - * to deal with multiple cores loaded at the same time. - * - Fixing this in general is TODO for an eventual libretro v2. - */ - void* handle; - - /* The Vulkan instance the context is using. */ - VkInstance instance; - /* The physical device used. */ - VkPhysicalDevice gpu; - /* The logical device used. */ - VkDevice device; - - /* Allows a core to fetch all its needed symbols without having to link - * against the loader itself. */ - PFN_vkGetDeviceProcAddr get_device_proc_addr; - PFN_vkGetInstanceProcAddr get_instance_proc_addr; - - /* The queue the core must use to submit data. - * This queue and index must remain constant throughout the lifetime - * of the context. - * - * This queue will be the queue that supports graphics and compute - * if the device supports compute. - */ - VkQueue queue; - unsigned queue_index; - - /* Before calling retro_video_refresh_t with RETRO_HW_FRAME_BUFFER_VALID, - * set which image to use for this frame. - * - * If num_semaphores is non-zero, the frontend will wait for the - * semaphores provided to be signaled before using the results further - * in the pipeline. - * - * Semaphores provided by a single call to set_image will only be - * waited for once (waiting for a semaphore resets it). - * E.g. set_image, video_refresh, and then another - * video_refresh without set_image, - * but same image will only wait for semaphores once. - * - * For this reason, ownership transfer will only occur if semaphores - * are waited on for a particular frame in the frontend. - * - * Using semaphores is optional for synchronization purposes, - * but if not using - * semaphores, an image memory barrier in vkCmdPipelineBarrier - * should be used in the graphics_queue. - * Example: - * - * vkCmdPipelineBarrier(cmd, - * srcStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - * dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - * image_memory_barrier = { - * srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - * dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - * }); - * - * The use of pipeline barriers instead of semaphores is encouraged - * as it is simpler and more fine-grained. A layout transition - * must generally happen anyways which requires a - * pipeline barrier. - * - * The image passed to set_image must have imageUsage flags set to at least - * VK_IMAGE_USAGE_TRANSFER_SRC_BIT and VK_IMAGE_USAGE_SAMPLED_BIT. - * The core will naturally want to use flags such as - * VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT and/or - * VK_IMAGE_USAGE_TRANSFER_DST_BIT depending - * on how the final image is created. - * - * The image must also have been created with MUTABLE_FORMAT bit set if - * 8-bit formats are used, so that the frontend can reinterpret sRGB - * formats as it sees fit. - * - * Images passed to set_image should be created with TILING_OPTIMAL. - * The image layout should be transitioned to either - * VK_IMAGE_LAYOUT_GENERIC or VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL. - * The actual image layout used must be set in image_layout. - * - * The image must be a 2D texture which may or not be layered - * and/or mipmapped. - * - * The image must be suitable for linear sampling. - * While the image_view is typically the only field used, - * the frontend may want to reinterpret the texture as sRGB vs. - * non-sRGB for example so the VkImageViewCreateInfo used to - * create the image view must also be passed in. - * - * The data in the pointer to the image struct will not be copied - * as the pNext field in create_info cannot be reliably deep-copied. - * The image pointer passed to set_image must be valid until - * retro_video_refresh_t has returned. - * - * If frame duping is used when passing NULL to retro_video_refresh_t, - * the frontend is free to either use the latest image passed to - * set_image or reuse the older pointer passed to set_image the - * frame RETRO_HW_FRAME_BUFFER_VALID was last used. - * - * Essentially, the lifetime of the pointer passed to - * set_image should be extended if frame duping is used - * so that the frontend can reuse the older pointer. - * - * The image itself however, must not be touched by the core until - * wait_sync_index has been completed later. The frontend may perform - * layout transitions on the image, so even read-only access is not defined. - * The exception to read-only rule is if GENERAL layout is used for the image. - * In this case, the frontend is not allowed to perform any layout transitions, - * so concurrent reads from core and frontend are allowed. - * - * If frame duping is used, or if set_command_buffers is used, - * the frontend will not wait for any semaphores. - * - * The src_queue_family is used to specify which queue family - * the image is currently owned by. If using multiple queue families - * (e.g. async compute), the frontend will need to acquire ownership of the - * image before rendering with it and release the image afterwards. - * - * If src_queue_family is equal to the queue family (queue_index), - * no ownership transfer will occur. - * Similarly, if src_queue_family is VK_QUEUE_FAMILY_IGNORED, - * no ownership transfer will occur. - * - * The frontend will always release ownership back to src_queue_family. - * Waiting for frontend to complete with wait_sync_index() ensures that - * the frontend has released ownership back to the application. - * Note that in Vulkan, transfering ownership is a two-part process. - * - * Example frame: - * - core releases ownership from src_queue_index to queue_index with VkImageMemoryBarrier. - * - core calls set_image with src_queue_index. - * - Frontend will acquire the image with src_queue_index -> queue_index as well, completing the - * ownership transfer. - * - Frontend renders the frame. - * - Frontend releases ownership with queue_index -> src_queue_index. - * - Next time image is used, core must acquire ownership from queue_index ... - * - * Since the frontend releases ownership, we cannot necessarily dupe the frame because - * the core needs to make the roundtrip of ownership transfer. - */ - retro_vulkan_set_image_t set_image; - - /* Get the current sync index for this frame which is obtained in - * frontend by calling e.g. vkAcquireNextImageKHR before calling - * retro_run(). - * - * This index will correspond to which swapchain buffer is currently - * the active one. - * - * Knowing this index is very useful for maintaining safe asynchronous CPU - * and GPU operation without stalling. - * - * The common pattern for synchronization is to receive fences when - * submitting command buffers to Vulkan (vkQueueSubmit) and add this fence - * to a list of fences for frame number get_sync_index(). - * - * Next time we receive the same get_sync_index(), we can wait for the - * fences from before, which will usually return immediately as the - * frontend will generally also avoid letting the GPU run ahead too much. - * - * After the fence has signaled, we know that the GPU has completed all - * GPU work related to work submitted in the frame we last saw get_sync_index(). - * - * This means we can safely reuse or free resources allocated in this frame. - * - * In theory, even if we wait for the fences correctly, it is not technically - * safe to write to the image we earlier passed to the frontend since we're - * not waiting for the frontend GPU jobs to complete. - * - * The frontend will guarantee that the appropriate pipeline barrier - * in graphics_queue has been used such that - * VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT cannot - * start until the frontend is done with the image. - */ - retro_vulkan_get_sync_index_t get_sync_index; - - /* Returns a bitmask of how many swapchain images we currently have - * in the frontend. - * - * If bit #N is set in the return value, get_sync_index can return N. - * Knowing this value is useful for preallocating per-frame management - * structures ahead of time. - * - * While this value will typically remain constant throughout the - * applications lifecycle, it may for example change if the frontend - * suddently changes fullscreen state and/or latency. - * - * If this value ever changes, it is safe to assume that the device - * is completely idle and all synchronization objects can be deleted - * right away as desired. - */ - retro_vulkan_get_sync_index_mask_t get_sync_index_mask; - - /* Instead of submitting the command buffer to the queue first, the core - * can pass along its command buffer to the frontend, and the frontend - * will submit the command buffer together with the frontends command buffers. - * - * This has the advantage that the overhead of vkQueueSubmit can be - * amortized into a single call. For this mode, semaphores in set_image - * will be ignored, so vkCmdPipelineBarrier must be used to synchronize - * the core and frontend. - * - * The command buffers in set_command_buffers are only executed once, - * even if frame duping is used. - * - * If frame duping is used, set_image should be used for the frames - * which should be duped instead. - * - * Command buffers passed to the frontend with set_command_buffers - * must not actually be submitted to the GPU until retro_video_refresh_t - * is called. - * - * The frontend must submit the command buffer before submitting any - * other command buffers provided by set_command_buffers. */ - retro_vulkan_set_command_buffers_t set_command_buffers; - - /* Waits on CPU for device activity for the current sync index to complete. - * This is useful since the core will not have a relevant fence to sync with - * when the frontend is submitting the command buffers. */ - retro_vulkan_wait_sync_index_t wait_sync_index; - - /* If the core submits command buffers itself to any of the queues provided - * in this interface, the core must lock and unlock the frontend from - * racing on the VkQueue. - * - * Queue submission can happen on any thread. - * Even if queue submission happens on the same thread as retro_run(), - * the lock/unlock functions must still be called. - * - * NOTE: Queue submissions are heavy-weight. */ - retro_vulkan_lock_queue_t lock_queue; - retro_vulkan_unlock_queue_t unlock_queue; - - /* Sets a semaphore which is signaled when the image in set_image can safely be reused. - * The semaphore is consumed next call to retro_video_refresh_t. - * The semaphore will be signalled even for duped frames. - * The semaphore will be signalled only once, so set_signal_semaphore should be called every - * frame. - * The semaphore may be VK_NULL_HANDLE, which disables semaphore signalling for next call to - * retro_video_refresh_t. - * - * This is mostly useful to support use cases where you're rendering to a single image that - * is recycled in a ping-pong fashion with the frontend to save memory (but potentially less - * throughput). - */ - retro_vulkan_set_signal_semaphore_t set_signal_semaphore; -}; - -#endif diff --git a/libretro/retro_inline.h b/libretro/retro_inline.h deleted file mode 100644 index b27d6dd6d4..0000000000 --- a/libretro/retro_inline.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (C) 2010-2020 The RetroArch team - * - * --------------------------------------------------------------------------------------- - * The following license statement only applies to this file (retro_inline.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_INLINE_H -#define __LIBRETRO_SDK_INLINE_H - -#ifndef INLINE - -#if defined(_WIN32) || defined(__INTEL_COMPILER) -#define INLINE __inline -#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L -#define INLINE inline -#elif defined(__GNUC__) -#define INLINE __inline__ -#else -#define INLINE -#endif - -#endif -#endif