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