#include "ppsspp_config.h"
#include "Common/Log.h"
#include "Common/File/FileUtil.h"
#include "Common/File/DirListing.h"
#include "Core/FileLoaders/LocalFileLoader.h"
#if PPSSPP_PLATFORM(ANDROID)
#include "android/jni/app-android.h"
#endif
#ifdef _WIN32
#include "Common/CommonWindows.h"
#if PPSSPP_PLATFORM(UWP)
#include <fileapifromapp.h>
#endif
#else
#include <fcntl.h>
#endif
#ifdef HAVE_LIBRETRO_VFS
#include <streams/file_stream.h>
#endif
#if !defined(_WIN32) && !defined(HAVE_LIBRETRO_VFS)
void LocalFileLoader::DetectSizeFd() {
#if PPSSPP_PLATFORM(ANDROID) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64)
off64_t off = lseek64(fd_, 0, SEEK_END);
filesize_ = off;
lseek64(fd_, 0, SEEK_SET);
#else
off_t off = lseek(fd_, 0, SEEK_END);
filesize_ = off;
lseek(fd_, 0, SEEK_SET);
#endif
}
#endif
LocalFileLoader::LocalFileLoader(const Path &filename)
: filesize_(0), filename_(filename) {
if (filename.empty()) {
ERROR_LOG(Log::FileSystem, "LocalFileLoader can't load empty filenames");
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);
VERBOSE_LOG(Log::System, "LocalFileLoader Fd %d for content URI: '%s'", fd, filename.c_str());
if (fd < 0) {
ERROR_LOG(Log::FileSystem, "LocalFileLoader failed to open content URI: '%s'", filename.c_str());
return;
}
fd_ = fd;
isOpenedByFd_ = true;
DetectSizeFd();
return;
}
#endif
#if defined(HAVE_LIBRETRO_VFS)
#elif !defined(_WIN32)
fd_ = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
if (fd_ == -1) {
return;
}
DetectSizeFd();
#else
const DWORD access = GENERIC_READ, share = FILE_SHARE_READ, mode = OPEN_EXISTING, flags = FILE_ATTRIBUTE_NORMAL;
#if PPSSPP_PLATFORM(UWP)
handle_ = CreateFile2FromAppW(filename.ToWString().c_str(), access, share, mode, nullptr);
#else
handle_ = CreateFile(filename.ToWString().c_str(), access, share, nullptr, mode, flags, nullptr);
#endif
if (handle_ == INVALID_HANDLE_VALUE) {
return;
}
LARGE_INTEGER end_offset;
const LARGE_INTEGER zero{};
if (SetFilePointerEx(handle_, zero, &end_offset, FILE_END) == 0) {
CloseHandle(handle_);
handle_ = INVALID_HANDLE_VALUE;
return;
}
filesize_ = end_offset.QuadPart;
SetFilePointerEx(handle_, zero, nullptr, FILE_BEGIN);
#endif
}
LocalFileLoader::~LocalFileLoader() {
#if defined(HAVE_LIBRETRO_VFS)
filestream_close(handle_);
#elif !defined(_WIN32)
if (fd_ != -1) {
close(fd_);
}
#else
if (handle_ != INVALID_HANDLE_VALUE) {
CloseHandle(handle_);
}
#endif
}
bool LocalFileLoader::Exists() {
#if defined(HAVE_LIBRETRO_VFS)
return handle_ != 0;
#elif !defined(_WIN32)
if (isOpenedByFd_) {
return fd_ != -1;
}
if (fd_ != -1)
return true;
#else
if (handle_ != INVALID_HANDLE_VALUE)
return true;
#endif
return File::Exists(filename_);
}
bool LocalFileLoader::IsDirectory() {
File::FileInfo info;
if (File::GetFileInfo(filename_, &info)) {
return info.exists && info.isDirectory;
}
return false;
}
s64 LocalFileLoader::FileSize() {
return filesize_;
}
size_t LocalFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags) {
if (bytes == 0)
return 0;
if (filesize_ == 0) {
ERROR_LOG(Log::FileSystem, "ReadAt from 0-sized file: %s", filename_.c_str());
return 0;
}
#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;
#elif PPSSPP_PLATFORM(SWITCH)
std::lock_guard<std::mutex> guard(readLock_);
lseek(fd_, absolutePos, SEEK_SET);
return read(fd_, data, bytes * count) / bytes;
#elif PPSSPP_PLATFORM(ANDROID)
if (absolutePos <= 0x7FFFFFFF) {
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64
return pread64(fd_, data, bytes * count, absolutePos) / bytes;
#else
return pread(fd_, data, bytes * count, absolutePos) / bytes;
#endif
} else {
std::lock_guard<std::mutex> guard(readLock_);
lseek64(fd_, absolutePos, SEEK_SET);
return read(fd_, data, bytes * count) / bytes;
}
#elif !defined(_WIN32)
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64
return pread64(fd_, data, bytes * count, absolutePos) / bytes;
#else
return pread(fd_, data, bytes * count, absolutePos) / bytes;
#endif
#else
DWORD read = -1;
OVERLAPPED offset = { 0 };
offset.Offset = (DWORD)(absolutePos & 0xffffffff);
offset.OffsetHigh = (DWORD)((absolutePos & 0xffffffff00000000) >> 32);
auto result = ReadFile(handle_, data, (DWORD)(bytes * count), &read, &offset);
return result == TRUE ? (size_t)read / bytes : -1;
#endif
}