Merge branch 'master' into aemu_postoffice_integrate_v2

This commit is contained in:
Henrik Rydgård
2026-01-19 11:40:38 +01:00
committed by GitHub
63 changed files with 951 additions and 4840 deletions
+14 -1
View File
@@ -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)
-1
View File
@@ -14,7 +14,6 @@
#endif
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
+41 -13
View File
@@ -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;
}
+169 -54
View File
@@ -77,6 +77,10 @@
#endif // !PPSSPP_PLATFORM(IOS)
#endif // __APPLE__
#ifdef HAVE_LIBRETRO_VFS
#include <file/file_path.h>
#endif
#include "Common/Data/Encoding/Utf8.h"
#include <sys/stat.h>
@@ -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) {
+27 -7
View File
@@ -17,7 +17,6 @@
#pragma once
#include <fstream>
#include <cstdio>
#include <string>
#include <string_view>
@@ -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 <typename T>
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 <typename T>
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
+4
View File
@@ -5,6 +5,10 @@
#include <string>
#include <string_view>
#ifdef HAVE_LIBRETRO_VFS
#include "libretro/LibretroFileStreamTransforms.h"
#endif
#if defined(__APPLE__)
#if TARGET_OS_IPHONE
+4 -4
View File
@@ -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) {
+132 -39
View File
@@ -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<std::mutex> 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;
}
+25 -2
View File
@@ -9,11 +9,34 @@
#include <mutex>
#include <set>
#include <string>
#include <utility>
#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<std::string> &files, std::set<std::string> &directories);
zip *zip_file_ = nullptr;
ZipContainer zip_file_;
std::mutex lock_;
std::string inZipPath_;
Path zipPath_;
+4
View File
@@ -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
+133
View File
@@ -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<SOCKET>(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)
+2
View File
@@ -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<std::string>& IP4s);
+2 -1
View File
@@ -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);
+2 -1
View File
@@ -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;
}
+2
View File
@@ -6,6 +6,8 @@
#include <mutex>
#include <cstdint>
#include "Common/File/Path.h"
class UIContext;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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),
+1 -1
View File
@@ -538,7 +538,7 @@ public:
// Networking
bool bEnableAdhocServer;
std::string proAdhocServer;
std::string sProAdhocServer;
bool bServerHasRelay;
std::vector<std::string> proAdhocServerList;
std::string sInfrastructureDNSServer;
+70 -19
View File
@@ -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;
}
+7 -37
View File
@@ -37,11 +37,6 @@
#include <fileapifromapp.h>
#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;
+12 -16
View File
@@ -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<std::mutex> guard(readLock_);
filestream_seek(handle_, absolutePos, RETRO_VFS_SEEK_POSITION_START);
return filestream_read(handle_, data, bytes * count) / bytes;
std::lock_guard<std::mutex> 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<std::mutex> guard(readLock_);
+4 -7
View File
@@ -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 <streams/file_stream.h>
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
+61 -9
View File
@@ -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);
}
+4 -2
View File
@@ -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;
+4 -4
View File
@@ -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;
}
+12 -6
View File
@@ -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));
+1 -1
View File
@@ -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);
}
+20 -5
View File
@@ -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) {
+8 -22
View File
@@ -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);
+4 -8
View File
@@ -20,13 +20,9 @@
#include <string>
#include <memory>
#ifdef SHARED_LIBZIP
#include <zip.h>
#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);
+2 -2
View File
@@ -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);
}
}
+3 -18
View File
@@ -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();
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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) {
+2 -2
View File
@@ -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?");
+2
View File
@@ -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;
+2 -2
View File
@@ -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;
+2
View File
@@ -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;
+1 -1
View File
@@ -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;
+2 -1
View File
@@ -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");
+2 -2
View File
@@ -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());
+2 -2
View File
@@ -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);
+62 -30
View File
@@ -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");
-1
View File
@@ -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;
+15 -5
View File
@@ -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<UIScreen>(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<UIScreen>(this, &UIScreen::OnBack);
settingsScroll->Add(settingsPane);
if (flags_ & TwoPaneFlags::SettingsToTheRight) {
+1
View File
@@ -38,6 +38,7 @@ enum class TwoPaneFlags {
SettingsCanScroll = 4,
ContentsCanScroll = 8,
CustomContextMenu = 16,
NoTopbarInLandscape = 32,
};
ENUM_CLASS_BITOPS(TwoPaneFlags);
+3
View File
@@ -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") {
+14
View File
@@ -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)
+2 -2
View File
@@ -1,12 +1,12 @@
#pragma once
#define HAVE_D3D11
#include "libretro/libretro_d3d.h"
#include <libretro_d3d.h>
#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;
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include <streams/file_stream_transforms.h>
#undef fopen
#undef fseek
#undef ftell
+1 -1
View File
@@ -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()) {
+1 -1
View File
@@ -1,7 +1,7 @@
#pragma once
#include <atomic>
#include "libretro/libretro.h"
#include <libretro.h>
#include "Common/GraphicsContext.h"
#include "Common/GPU/thin3d_create.h"
+3 -1
View File
@@ -10,9 +10,11 @@
#include "Common/Data/Text/Parsers.h"
#include "libretro/LibretroVulkanContext.h"
#include "libretro/libretro_vulkan.h"
#include <libretro_vulkan.h>
#include <GPU/Vulkan/VulkanRenderManager.h>
#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);
+7 -3
View File
@@ -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)))
-1
View File
@@ -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
+38 -26
View File
@@ -46,7 +46,7 @@
#include "UI/AudioCommon.h"
#include "libretro/libretro.h"
#include <libretro.h>
#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<char>('0' + addressPt);
g_Config.sProAdhocServer += static_cast<char>('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<std::string> 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<uint8_t, 4096> 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;
-3968
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -4,8 +4,8 @@
#include <stdlib.h>
#include <string.h>
#include "libretro.h"
#include "retro_inline.h"
#include <libretro.h>
#include <retro_inline.h>
#ifndef HAVE_NO_LANGEXTRA
#include "libretro_core_options_intl.h"
-60
View File
@@ -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 <d3d11.h>
#include <D3Dcompiler.h>
#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__ */
+2 -1
View File
@@ -9,7 +9,8 @@
#include "Core/Config.h"
#define VK_NO_PROTOTYPES
#include "libretro/libretro_vulkan.h"
#include <libretro_vulkan.h>
#include "libretro/LibretroGraphicsContext.h"
using namespace PPSSPP_VK;
-404
View File
@@ -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
-39
View File
@@ -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