// 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 #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" #ifdef _WIN32 #include "Common/CommonWindows.h" #else #include #endif #ifndef _WIN32 LocalFileLoader::LocalFileLoader(int fd, const Path &filename) : fd_(fd), filename_(filename), isOpenedByFd_(fd != -1) { if (fd != -1) { DetectSizeFd(); } } 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), isOpenedByFd_(false) { if (filename.empty()) { ERROR_LOG(FILESYS, "LocalFileLoader can't load empty filenames"); return; } #ifndef _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_ = CreateFile2(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() { #ifndef _WIN32 if (fd_ != -1) { close(fd_); } #else if (handle_ != INVALID_HANDLE_VALUE) { CloseHandle(handle_); } #endif } bool LocalFileLoader::Exists() { // If we couldn't open it for reading, we say it does not exist. #ifndef _WIN32 if (isOpenedByFd_) { return true; } if (fd_ != -1 || IsDirectory()) { #else if (handle_ != INVALID_HANDLE_VALUE || IsDirectory()) { #endif File::FileInfo info; return File::GetFileInfo(filename_, &info); } return false; } bool LocalFileLoader::IsDirectory() { File::FileInfo info; if (File::GetFileInfo(filename_, &info)) { return 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 PPSSPP_PLATFORM(SWITCH) // Toolchain has no fancy IO API. We must lock. std::lock_guard 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 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 }