Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/FileLoaders/LocalFileLoader.cpp
3186 views
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
19
#include "ppsspp_config.h"
20
21
#include "Common/Log.h"
22
#include "Common/File/FileUtil.h"
23
#include "Common/File/DirListing.h"
24
#include "Core/FileLoaders/LocalFileLoader.h"
25
26
#if PPSSPP_PLATFORM(ANDROID)
27
#include "android/jni/app-android.h"
28
#endif
29
30
#ifdef _WIN32
31
#include "Common/CommonWindows.h"
32
#if PPSSPP_PLATFORM(UWP)
33
#include <fileapifromapp.h>
34
#endif
35
#else
36
#include <fcntl.h>
37
#endif
38
39
#ifdef HAVE_LIBRETRO_VFS
40
#include <streams/file_stream.h>
41
#endif
42
43
#if !defined(_WIN32) && !defined(HAVE_LIBRETRO_VFS)
44
45
void LocalFileLoader::DetectSizeFd() {
46
#if PPSSPP_PLATFORM(ANDROID) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64)
47
off64_t off = lseek64(fd_, 0, SEEK_END);
48
filesize_ = off;
49
lseek64(fd_, 0, SEEK_SET);
50
#else
51
off_t off = lseek(fd_, 0, SEEK_END);
52
filesize_ = off;
53
lseek(fd_, 0, SEEK_SET);
54
#endif
55
}
56
#endif
57
58
LocalFileLoader::LocalFileLoader(const Path &filename)
59
: filesize_(0), filename_(filename) {
60
if (filename.empty()) {
61
ERROR_LOG(Log::FileSystem, "LocalFileLoader can't load empty filenames");
62
return;
63
}
64
65
#if HAVE_LIBRETRO_VFS
66
isOpenedByFd_ = false;
67
handle_ = filestream_open(filename.c_str(), RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
68
filestream_seek(handle_, 0, RETRO_VFS_SEEK_POSITION_END);
69
filesize_ = filestream_tell(handle_);
70
filestream_seek(handle_, 0, RETRO_VFS_SEEK_POSITION_START);
71
return;
72
#endif
73
74
#if PPSSPP_PLATFORM(ANDROID) && !defined(HAVE_LIBRETRO_VFS)
75
if (filename.Type() == PathType::CONTENT_URI) {
76
int fd = Android_OpenContentUriFd(filename.ToString(), Android_OpenContentUriMode::READ);
77
VERBOSE_LOG(Log::System, "LocalFileLoader Fd %d for content URI: '%s'", fd, filename.c_str());
78
if (fd < 0) {
79
ERROR_LOG(Log::FileSystem, "LocalFileLoader failed to open content URI: '%s'", filename.c_str());
80
return;
81
}
82
fd_ = fd;
83
isOpenedByFd_ = true;
84
DetectSizeFd();
85
return;
86
}
87
#endif
88
89
#if defined(HAVE_LIBRETRO_VFS)
90
// Nothing to do here...
91
#elif !defined(_WIN32)
92
93
fd_ = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
94
if (fd_ == -1) {
95
return;
96
}
97
98
DetectSizeFd();
99
100
#else // _WIN32
101
102
const DWORD access = GENERIC_READ, share = FILE_SHARE_READ, mode = OPEN_EXISTING, flags = FILE_ATTRIBUTE_NORMAL;
103
#if PPSSPP_PLATFORM(UWP)
104
handle_ = CreateFile2FromAppW(filename.ToWString().c_str(), access, share, mode, nullptr);
105
#else
106
handle_ = CreateFile(filename.ToWString().c_str(), access, share, nullptr, mode, flags, nullptr);
107
#endif
108
if (handle_ == INVALID_HANDLE_VALUE) {
109
return;
110
}
111
LARGE_INTEGER end_offset;
112
const LARGE_INTEGER zero{};
113
if (SetFilePointerEx(handle_, zero, &end_offset, FILE_END) == 0) {
114
// Couldn't seek in the file. Close it and give up? This should never happen.
115
CloseHandle(handle_);
116
handle_ = INVALID_HANDLE_VALUE;
117
return;
118
}
119
filesize_ = end_offset.QuadPart;
120
SetFilePointerEx(handle_, zero, nullptr, FILE_BEGIN);
121
#endif // _WIN32
122
}
123
124
LocalFileLoader::~LocalFileLoader() {
125
#if defined(HAVE_LIBRETRO_VFS)
126
filestream_close(handle_);
127
#elif !defined(_WIN32)
128
if (fd_ != -1) {
129
close(fd_);
130
}
131
#else
132
if (handle_ != INVALID_HANDLE_VALUE) {
133
CloseHandle(handle_);
134
}
135
#endif
136
}
137
138
bool LocalFileLoader::Exists() {
139
// If we opened it for reading, it must exist. Done.
140
#if defined(HAVE_LIBRETRO_VFS)
141
return handle_ != 0;
142
143
#elif !defined(_WIN32)
144
if (isOpenedByFd_) {
145
// As an optimization, if we already tried and failed, quickly return.
146
// This is used because Android Content URIs are so slow.
147
return fd_ != -1;
148
}
149
if (fd_ != -1)
150
return true;
151
#else
152
if (handle_ != INVALID_HANDLE_VALUE)
153
return true;
154
#endif
155
156
return File::Exists(filename_);
157
}
158
159
bool LocalFileLoader::IsDirectory() {
160
File::FileInfo info;
161
if (File::GetFileInfo(filename_, &info)) {
162
return info.exists && info.isDirectory;
163
}
164
return false;
165
}
166
167
s64 LocalFileLoader::FileSize() {
168
return filesize_;
169
}
170
171
size_t LocalFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags) {
172
if (bytes == 0)
173
return 0;
174
175
if (filesize_ == 0) {
176
ERROR_LOG(Log::FileSystem, "ReadAt from 0-sized file: %s", filename_.c_str());
177
return 0;
178
}
179
180
#if defined(HAVE_LIBRETRO_VFS)
181
std::lock_guard<std::mutex> guard(readLock_);
182
filestream_seek(handle_, absolutePos, RETRO_VFS_SEEK_POSITION_START);
183
return filestream_read(handle_, data, bytes * count) / bytes;
184
#elif PPSSPP_PLATFORM(SWITCH)
185
// Toolchain has no fancy IO API. We must lock.
186
std::lock_guard<std::mutex> guard(readLock_);
187
lseek(fd_, absolutePos, SEEK_SET);
188
return read(fd_, data, bytes * count) / bytes;
189
#elif PPSSPP_PLATFORM(ANDROID)
190
// pread64 doesn't appear to actually be 64-bit safe, though such ISOs are uncommon. See #10862.
191
if (absolutePos <= 0x7FFFFFFF) {
192
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64
193
return pread64(fd_, data, bytes * count, absolutePos) / bytes;
194
#else
195
return pread(fd_, data, bytes * count, absolutePos) / bytes;
196
#endif
197
} else {
198
// Since pread64 doesn't change the file offset, it should be safe to avoid the lock in the common case.
199
std::lock_guard<std::mutex> guard(readLock_);
200
lseek64(fd_, absolutePos, SEEK_SET);
201
return read(fd_, data, bytes * count) / bytes;
202
}
203
#elif !defined(_WIN32)
204
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64
205
return pread64(fd_, data, bytes * count, absolutePos) / bytes;
206
#else
207
return pread(fd_, data, bytes * count, absolutePos) / bytes;
208
#endif
209
#else
210
DWORD read = -1;
211
OVERLAPPED offset = { 0 };
212
offset.Offset = (DWORD)(absolutePos & 0xffffffff);
213
offset.OffsetHigh = (DWORD)((absolutePos & 0xffffffff00000000) >> 32);
214
auto result = ReadFile(handle_, data, (DWORD)(bytes * count), &read, &offset);
215
return result == TRUE ? (size_t)read / bytes : -1;
216
#endif
217
}
218
219