mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Merge branch 'master' into aemu_postoffice_integrate_v2
This commit is contained in:
+14
-1
@@ -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)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <mutex>
|
||||
#include <cstdint>
|
||||
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
|
||||
class UIContext;
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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?");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -38,6 +38,7 @@ enum class TwoPaneFlags {
|
||||
SettingsCanScroll = 4,
|
||||
ContentsCanScroll = 8,
|
||||
CustomContextMenu = 16,
|
||||
NoTopbarInLandscape = 32,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(TwoPaneFlags);
|
||||
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <streams/file_stream_transforms.h>
|
||||
#undef fopen
|
||||
#undef fseek
|
||||
#undef ftell
|
||||
@@ -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,7 +1,7 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
|
||||
#include "libretro/libretro.h"
|
||||
#include <libretro.h>
|
||||
#include "Common/GraphicsContext.h"
|
||||
#include "Common/GPU/thin3d_create.h"
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
Submodule libretro/libretro-common updated: 2a4b12d1a3...76a3d54feb
+38
-26
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
|
||||
@@ -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__ */
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user