CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/FileLoaders/LocalFileLoader.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <cstdio>
19
20
#include "ppsspp_config.h"
21
22
#include "Common/Data/Encoding/Utf8.h"
23
#include "Common/Log.h"
24
#include "Common/File/FileUtil.h"
25
#include "Common/File/DirListing.h"
26
#include "Core/FileLoaders/LocalFileLoader.h"
27
28
#if PPSSPP_PLATFORM(ANDROID)
29
#include "android/jni/app-android.h"
30
#endif
31
32
#ifdef _WIN32
33
#include "Common/CommonWindows.h"
34
#if PPSSPP_PLATFORM(UWP)
35
#include <fileapifromapp.h>
36
#endif
37
#else
38
#include <fcntl.h>
39
#endif
40
41
#ifdef HAVE_LIBRETRO_VFS
42
#include <streams/file_stream.h>
43
#endif
44
45
#if !defined(_WIN32) && !defined(HAVE_LIBRETRO_VFS)
46
47
void LocalFileLoader::DetectSizeFd() {
48
#if PPSSPP_PLATFORM(ANDROID) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64)
49
off64_t off = lseek64(fd_, 0, SEEK_END);
50
filesize_ = off;
51
lseek64(fd_, 0, SEEK_SET);
52
#else
53
off_t off = lseek(fd_, 0, SEEK_END);
54
filesize_ = off;
55
lseek(fd_, 0, SEEK_SET);
56
#endif
57
}
58
#endif
59
60
LocalFileLoader::LocalFileLoader(const Path &filename)
61
: filesize_(0), filename_(filename) {
62
if (filename.empty()) {
63
ERROR_LOG(Log::FileSystem, "LocalFileLoader can't load empty filenames");
64
return;
65
}
66
67
#if HAVE_LIBRETRO_VFS
68
isOpenedByFd_ = false;
69
handle_ = filestream_open(filename.c_str(), RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
70
filestream_seek(handle_, 0, RETRO_VFS_SEEK_POSITION_END);
71
filesize_ = filestream_tell(handle_);
72
filestream_seek(handle_, 0, RETRO_VFS_SEEK_POSITION_START);
73
return;
74
#endif
75
76
#if PPSSPP_PLATFORM(ANDROID) && !defined(HAVE_LIBRETRO_VFS)
77
if (filename.Type() == PathType::CONTENT_URI) {
78
int fd = Android_OpenContentUriFd(filename.ToString(), Android_OpenContentUriMode::READ);
79
VERBOSE_LOG(Log::System, "LocalFileLoader Fd %d for content URI: '%s'", fd, filename.c_str());
80
if (fd < 0) {
81
ERROR_LOG(Log::FileSystem, "LocalFileLoader failed to open content URI: '%s'", filename.c_str());
82
return;
83
}
84
fd_ = fd;
85
isOpenedByFd_ = true;
86
DetectSizeFd();
87
return;
88
}
89
#endif
90
91
#if defined(HAVE_LIBRETRO_VFS)
92
// Nothing to do here...
93
#elif !defined(_WIN32)
94
95
fd_ = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
96
if (fd_ == -1) {
97
return;
98
}
99
100
DetectSizeFd();
101
102
#else // _WIN32
103
104
const DWORD access = GENERIC_READ, share = FILE_SHARE_READ, mode = OPEN_EXISTING, flags = FILE_ATTRIBUTE_NORMAL;
105
#if PPSSPP_PLATFORM(UWP)
106
handle_ = CreateFile2FromAppW(filename.ToWString().c_str(), access, share, mode, nullptr);
107
#else
108
handle_ = CreateFile(filename.ToWString().c_str(), access, share, nullptr, mode, flags, nullptr);
109
#endif
110
if (handle_ == INVALID_HANDLE_VALUE) {
111
return;
112
}
113
LARGE_INTEGER end_offset;
114
const LARGE_INTEGER zero{};
115
if (SetFilePointerEx(handle_, zero, &end_offset, FILE_END) == 0) {
116
// Couldn't seek in the file. Close it and give up? This should never happen.
117
CloseHandle(handle_);
118
handle_ = INVALID_HANDLE_VALUE;
119
return;
120
}
121
filesize_ = end_offset.QuadPart;
122
SetFilePointerEx(handle_, zero, nullptr, FILE_BEGIN);
123
#endif // _WIN32
124
}
125
126
LocalFileLoader::~LocalFileLoader() {
127
#if defined(HAVE_LIBRETRO_VFS)
128
filestream_close(handle_);
129
#elif !defined(_WIN32)
130
if (fd_ != -1) {
131
close(fd_);
132
}
133
#else
134
if (handle_ != INVALID_HANDLE_VALUE) {
135
CloseHandle(handle_);
136
}
137
#endif
138
}
139
140
bool LocalFileLoader::Exists() {
141
// If we opened it for reading, it must exist. Done.
142
#if defined(HAVE_LIBRETRO_VFS)
143
return handle_ != 0;
144
145
#elif !defined(_WIN32)
146
if (isOpenedByFd_) {
147
// As an optimization, if we already tried and failed, quickly return.
148
// This is used because Android Content URIs are so slow.
149
return fd_ != -1;
150
}
151
if (fd_ != -1)
152
return true;
153
#else
154
if (handle_ != INVALID_HANDLE_VALUE)
155
return true;
156
#endif
157
158
File::FileInfo info;
159
if (File::GetFileInfo(filename_, &info)) {
160
return info.exists;
161
} else {
162
return false;
163
}
164
}
165
166
bool LocalFileLoader::IsDirectory() {
167
File::FileInfo info;
168
if (File::GetFileInfo(filename_, &info)) {
169
return info.exists && info.isDirectory;
170
}
171
return false;
172
}
173
174
s64 LocalFileLoader::FileSize() {
175
return filesize_;
176
}
177
178
size_t LocalFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags) {
179
if (bytes == 0)
180
return 0;
181
182
if (filesize_ == 0) {
183
ERROR_LOG(Log::FileSystem, "ReadAt from 0-sized file: %s", filename_.c_str());
184
return 0;
185
}
186
187
#if defined(HAVE_LIBRETRO_VFS)
188
std::lock_guard<std::mutex> guard(readLock_);
189
filestream_seek(handle_, absolutePos, RETRO_VFS_SEEK_POSITION_START);
190
return filestream_read(handle_, data, bytes * count) / bytes;
191
#elif PPSSPP_PLATFORM(SWITCH)
192
// Toolchain has no fancy IO API. We must lock.
193
std::lock_guard<std::mutex> guard(readLock_);
194
lseek(fd_, absolutePos, SEEK_SET);
195
return read(fd_, data, bytes * count) / bytes;
196
#elif PPSSPP_PLATFORM(ANDROID)
197
// pread64 doesn't appear to actually be 64-bit safe, though such ISOs are uncommon. See #10862.
198
if (absolutePos <= 0x7FFFFFFF) {
199
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64
200
return pread64(fd_, data, bytes * count, absolutePos) / bytes;
201
#else
202
return pread(fd_, data, bytes * count, absolutePos) / bytes;
203
#endif
204
} else {
205
// Since pread64 doesn't change the file offset, it should be safe to avoid the lock in the common case.
206
std::lock_guard<std::mutex> guard(readLock_);
207
lseek64(fd_, absolutePos, SEEK_SET);
208
return read(fd_, data, bytes * count) / bytes;
209
}
210
#elif !defined(_WIN32)
211
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64
212
return pread64(fd_, data, bytes * count, absolutePos) / bytes;
213
#else
214
return pread(fd_, data, bytes * count, absolutePos) / bytes;
215
#endif
216
#else
217
DWORD read = -1;
218
OVERLAPPED offset = { 0 };
219
offset.Offset = (DWORD)(absolutePos & 0xffffffff);
220
offset.OffsetHigh = (DWORD)((absolutePos & 0xffffffff00000000) >> 32);
221
auto result = ReadFile(handle_, data, (DWORD)(bytes * count), &read, &offset);
222
return result == TRUE ? (size_t)read / bytes : -1;
223
#endif
224
}
225
226