mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-12-14 08:59:47 +00:00
225 lines
6.4 KiB
C++
225 lines
6.4 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#include <cstdio>
|
|
|
|
#include "ppsspp_config.h"
|
|
|
|
#include "Common/Data/Encoding/Utf8.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(FILESYS, "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(SYSTEM, "LocalFileLoader Fd %d for content URI: '%s'", fd, filename.c_str());
|
|
if (fd < 0) {
|
|
ERROR_LOG(FILESYS, "LocalFileLoader failed to open content URI: '%s'", filename.c_str());
|
|
return;
|
|
}
|
|
fd_ = fd;
|
|
isOpenedByFd_ = true;
|
|
DetectSizeFd();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_LIBRETRO_VFS)
|
|
// Nothing to do here...
|
|
#elif !defined(_WIN32)
|
|
|
|
fd_ = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
|
|
if (fd_ == -1) {
|
|
return;
|
|
}
|
|
|
|
DetectSizeFd();
|
|
|
|
#else // _WIN32
|
|
|
|
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) {
|
|
// Couldn't seek in the file. Close it and give up? This should never happen.
|
|
CloseHandle(handle_);
|
|
handle_ = INVALID_HANDLE_VALUE;
|
|
return;
|
|
}
|
|
filesize_ = end_offset.QuadPart;
|
|
SetFilePointerEx(handle_, zero, nullptr, FILE_BEGIN);
|
|
#endif // _WIN32
|
|
}
|
|
|
|
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 we opened it for reading, it must exist. Done.
|
|
#if defined(HAVE_LIBRETRO_VFS)
|
|
return handle_ != 0;
|
|
|
|
#elif !defined(_WIN32)
|
|
if (isOpenedByFd_) {
|
|
// As an optimization, if we already tried and failed, quickly return.
|
|
// This is used because Android Content URIs are so slow.
|
|
return fd_ != -1;
|
|
}
|
|
if (fd_ != -1)
|
|
return true;
|
|
#else
|
|
if (handle_ != INVALID_HANDLE_VALUE)
|
|
return true;
|
|
#endif
|
|
|
|
File::FileInfo info;
|
|
if (File::GetFileInfo(filename_, &info)) {
|
|
return info.exists;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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(FILESYS, "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)
|
|
// Toolchain has no fancy IO API. We must lock.
|
|
std::lock_guard<std::mutex> guard(readLock_);
|
|
lseek(fd_, absolutePos, SEEK_SET);
|
|
return read(fd_, data, bytes * count) / bytes;
|
|
#elif PPSSPP_PLATFORM(ANDROID)
|
|
// pread64 doesn't appear to actually be 64-bit safe, though such ISOs are uncommon. See #10862.
|
|
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 {
|
|
// Since pread64 doesn't change the file offset, it should be safe to avoid the lock in the common case.
|
|
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
|
|
}
|