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/UWP/UWPHelpers/StorageManager.cpp
Views: 1401
1
// Copyright (c) 2023- 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 "pch.h"
19
#include <io.h>
20
#include <fcntl.h>
21
#include <collection.h>
22
23
#include "Common/Log.h"
24
#include "Core/Config.h"
25
#include "Common/File/Path.h"
26
#include "Common/StringUtils.h"
27
#include "UWPUtil.h"
28
29
#include "StorageManager.h"
30
#include "StorageAsync.h"
31
#include "StorageAccess.h"
32
33
34
using namespace Platform;
35
using namespace Windows::Storage;
36
using namespace Windows::Foundation;
37
using namespace Windows::ApplicationModel;
38
39
40
#pragma region Locations
41
std::string GetPSPFolder() {
42
if (g_Config.memStickDirectory.empty()) {
43
return g_Config.internalDataDirectory.ToString();
44
}
45
else {
46
return g_Config.memStickDirectory.ToString();
47
}
48
}
49
std::string GetInstallationFolder() {
50
return FromPlatformString(Package::Current->InstalledLocation->Path);
51
}
52
StorageFolder^ GetLocalStorageFolder() {
53
return ApplicationData::Current->LocalFolder;
54
}
55
std::string GetLocalFolder() {
56
return FromPlatformString(GetLocalStorageFolder()->Path);
57
}
58
std::string GetTempFolder() {
59
return FromPlatformString(ApplicationData::Current->TemporaryFolder->Path);
60
}
61
std::string GetTempFile(std::string name) {
62
StorageFile^ tmpFile;
63
ExecuteTask(tmpFile, ApplicationData::Current->TemporaryFolder->CreateFileAsync(ToPlatformString(name), CreationCollisionOption::GenerateUniqueName));
64
if (tmpFile != nullptr) {
65
return FromPlatformString(tmpFile->Path);
66
}
67
else {
68
return "";
69
}
70
}
71
std::string GetPicturesFolder() {
72
// Requires 'picturesLibrary' capability
73
return FromPlatformString(KnownFolders::PicturesLibrary->Path);
74
}
75
std::string GetVideosFolder() {
76
// Requires 'videosLibrary' capability
77
return FromPlatformString(KnownFolders::VideosLibrary->Path);
78
}
79
std::string GetDocumentsFolder() {
80
// Requires 'documentsLibrary' capability
81
return FromPlatformString(KnownFolders::DocumentsLibrary->Path);
82
}
83
std::string GetMusicFolder() {
84
// Requires 'musicLibrary' capability
85
return FromPlatformString(KnownFolders::MusicLibrary->Path);
86
}
87
std::string GetPreviewPath(std::string path) {
88
std::string pathView = path;
89
pathView = ReplaceAll(pathView, "/", "\\");
90
std::string currentMemoryStick = GetPSPFolder();
91
// Ensure memStick sub path replaced by 'ms:'
92
pathView = ReplaceAll(pathView, currentMemoryStick + "\\", "ms:\\");
93
auto appData = ReplaceAll(GetLocalFolder(), "\\LocalState", "");
94
pathView = ReplaceAll(pathView, appData, "AppData");
95
96
return pathView;
97
}
98
bool isLocalState(std::string path) {
99
return !_stricmp(GetPreviewPath(path).c_str(), "LocalState");
100
}
101
#pragma endregion
102
103
#pragma region Internal
104
Path PathResolver(Path path) {
105
auto root = path.GetDirectory();
106
auto newPath = path.ToString();
107
if (path.IsRoot() || !_stricmp(root.c_str(), "/") || !_stricmp(root.c_str(), "\\")) {
108
// System requesting file from app data
109
newPath = ReplaceAll(newPath, "/", (GetLocalFolder() + (path.size() > 1 ? "/" : "")));
110
}
111
return Path(newPath);
112
}
113
Path PathResolver(std::string path) {
114
return PathResolver(Path(path));
115
}
116
117
std::string ResolvePathUWP(std::string path) {
118
return PathResolver(path).ToString();
119
}
120
#pragma endregion
121
122
#pragma region Functions
123
std::map<std::string, bool> accessState;
124
bool CheckDriveAccess(std::string driveName) {
125
bool state = false;
126
127
HANDLE searchResults;
128
WIN32_FIND_DATA findDataResult;
129
auto keyIter = accessState.find(driveName);
130
if (keyIter != accessState.end()) {
131
state = keyIter->second;
132
}
133
else {
134
try {
135
wchar_t* filteredPath = _wcsdup(ConvertUTF8ToWString(driveName).c_str());
136
wcscat_s(filteredPath, sizeof(L"\\*.*"), L"\\*.*");
137
138
searchResults = FindFirstFileExFromAppW(
139
filteredPath, FindExInfoBasic, &findDataResult,
140
FindExSearchNameMatch, NULL, 0);
141
142
state = searchResults != NULL && searchResults != INVALID_HANDLE_VALUE;
143
if (state) {
144
FindClose(searchResults);
145
}
146
// Cache the state
147
accessState.insert(std::make_pair(driveName, state));
148
}
149
catch (...) {
150
}
151
}
152
if (!state) {
153
state = IsRootForAccessibleItems(driveName);
154
}
155
return state;
156
}
157
158
FILE* GetFileStreamFromApp(std::string path, const char* mode) {
159
160
FILE* file{};
161
162
auto pathResolved = Path(ResolvePathUWP(path));
163
HANDLE handle;
164
165
DWORD dwDesiredAccess = GENERIC_READ;
166
DWORD dwShareMode = FILE_SHARE_READ;
167
DWORD dwCreationDisposition = OPEN_EXISTING;
168
int flags = 0;
169
170
if (!strcmp(mode, "r") || !strcmp(mode, "rb") || !strcmp(mode, "rt"))
171
{
172
dwDesiredAccess = GENERIC_READ;
173
dwShareMode = FILE_SHARE_READ;
174
dwCreationDisposition = OPEN_EXISTING;
175
flags = _O_RDONLY;
176
}
177
else if (!strcmp(mode, "r+") || !strcmp(mode, "rb+") || !strcmp(mode, "r+b") || !strcmp(mode, "rt+") || !strcmp(mode, "r+t"))
178
{
179
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
180
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
181
dwCreationDisposition = OPEN_EXISTING;
182
flags = _O_RDWR;
183
}
184
else if (!strcmp(mode, "a") || !strcmp(mode, "ab") || !strcmp(mode, "at")) {
185
dwDesiredAccess = GENERIC_WRITE;
186
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
187
dwCreationDisposition = OPEN_ALWAYS;
188
flags = _O_APPEND | _O_WRONLY | _O_CREAT;
189
}
190
else if (!strcmp(mode, "a+") || !strcmp(mode, "ab+") || !strcmp(mode, "a+b") || !strcmp(mode, "at+") || !strcmp(mode, "a+t")) {
191
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
192
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
193
dwCreationDisposition = OPEN_ALWAYS;
194
flags = _O_APPEND | _O_RDWR | _O_CREAT;
195
}
196
else if (!strcmp(mode, "w") || !strcmp(mode, "wb") || !strcmp(mode, "wt"))
197
{
198
dwDesiredAccess = GENERIC_WRITE;
199
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
200
dwCreationDisposition = CREATE_ALWAYS;
201
flags = _O_WRONLY | _O_CREAT | _O_TRUNC;
202
}
203
else if (!strcmp(mode, "w+") || !strcmp(mode, "wb+") || !strcmp(mode, "w+b") || !strcmp(mode, "wt+") || !strcmp(mode, "w+t"))
204
{
205
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
206
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
207
dwCreationDisposition = CREATE_ALWAYS;
208
flags = _O_RDWR | _O_CREAT | _O_TRUNC;
209
}
210
211
if (strpbrk(mode, "t") != nullptr) {
212
flags |= _O_TEXT;
213
}
214
215
handle = CreateFile2FromAppW(pathResolved.ToWString().c_str(), dwDesiredAccess, dwShareMode, dwCreationDisposition, nullptr);
216
217
if (handle != INVALID_HANDLE_VALUE) {
218
file = _fdopen(_open_osfhandle((intptr_t)handle, flags), mode);
219
}
220
221
return file;
222
}
223
224
#pragma endregion
225
226
#pragma region FakeFolders
227
// Parent and child full path
228
std::string getSubRoot(std::string parent, std::string child) {
229
auto childCut = child;
230
childCut = ReplaceAll(childCut, (parent + "/"), "");
231
size_t len = childCut.find_first_of('/', 0);
232
auto subRoot = childCut.substr(0, len);
233
234
return parent + "/" + subRoot;
235
}
236
237
bool isChild(std::string parent, std::string child) {
238
return child.find(parent) != std::string::npos;
239
}
240
241
// Parent full path, child full path, child name only
242
bool isParent(std::string parent, std::string child, std::string childName) {
243
parent.append("/" + childName);
244
return parent == child;
245
}
246
247
bool IsRootForAccessibleItems(Path path, std::list<std::string>& subRoot, bool breakOnFirstMatch = false) {
248
path = PathResolver(path);
249
auto FutureAccessItems = GetFutureAccessList();
250
for (auto& fItem : FutureAccessItems) {
251
if (isChild(path.ToString(), fItem)) {
252
if (breakOnFirstMatch) {
253
// Just checking, we don't need to loop for each item
254
return true;
255
}
256
auto sub = getSubRoot(path.ToString(), fItem);
257
258
// This check can be better, but that's how I can do it in C++
259
if (!endsWith(sub, ":")) {
260
bool alreadyAdded = false;
261
for each (auto sItem in subRoot) {
262
if (!strcmp(sItem.c_str(), sub.c_str())) {
263
alreadyAdded = true;
264
break;
265
}
266
}
267
if (!alreadyAdded) {
268
subRoot.push_back(sub);
269
}
270
}
271
}
272
}
273
return !subRoot.empty();
274
}
275
bool IsRootForAccessibleItems(std::string path)
276
{
277
std::list<std::string> tmp;
278
return IsRootForAccessibleItems(Path(path), tmp, true);
279
}
280
281
bool GetFakeFolders(Path path, std::vector<File::FileInfo>* files, const char* filter, std::set<std::string> filters) {
282
bool state = false;
283
std::list<std::string> subRoot;
284
if (IsRootForAccessibleItems(path, subRoot)) {
285
if (!subRoot.empty()) {
286
for each (auto sItem in subRoot) {
287
auto folderPath = Path(sItem);
288
File::FileInfo info;
289
info.name = folderPath.GetFilename();
290
info.fullName = folderPath;
291
info.exists = true;
292
info.size = 1;
293
info.isDirectory = true;
294
info.isWritable = true;
295
info.atime = 0;
296
info.mtime = 0;
297
info.ctime = 0;
298
info.access = 0111;
299
300
files->push_back(info);
301
state = true;
302
}
303
}
304
}
305
return state;
306
}
307
308
#pragma endregion
309
310
#pragma region Helpers
311
bool OpenFile(std::string path) {
312
bool state = false;
313
Platform::String^ wString = ref new Platform::String(Path(path).ToWString().c_str());
314
315
StorageFile^ storageItem;
316
ExecuteTask(storageItem, StorageFile::GetFileFromPathAsync(wString));
317
if (storageItem != nullptr) {
318
ExecuteTask(state, Windows::System::Launcher::LaunchFileAsync(storageItem), false);
319
}
320
else {
321
auto uri = ref new Windows::Foundation::Uri(wString);
322
ExecuteTask(state, Windows::System::Launcher::LaunchUriAsync(uri), false);
323
}
324
return state;
325
}
326
327
bool OpenFolder(std::string path) {
328
bool state = false;
329
Path itemPath(path);
330
Platform::String^ wString = ref new Platform::String(itemPath.ToWString().c_str());
331
StorageFolder^ storageItem;
332
ExecuteTask(storageItem, StorageFolder::GetFolderFromPathAsync(wString));
333
if (storageItem != nullptr) {
334
ExecuteTask(state, Windows::System::Launcher::LaunchFolderAsync(storageItem), false);
335
}
336
else {
337
// Try as it's file
338
Path parent = Path(itemPath.GetDirectory());
339
Platform::String^ wParentString = ref new Platform::String(parent.ToWString().c_str());
340
341
ExecuteTask(storageItem, StorageFolder::GetFolderFromPathAsync(wParentString));
342
if (storageItem != nullptr) {
343
ExecuteTask(state, Windows::System::Launcher::LaunchFolderAsync(storageItem), false);
344
}
345
}
346
return state;
347
}
348
349
bool GetDriveFreeSpace(Path path, int64_t& space) {
350
351
bool state = false;
352
if (path.empty()) {
353
// This case happen on first start only
354
path = Path(GetPSPFolder());
355
if (g_Config.memStickDirectory.empty()) {
356
g_Config.memStickDirectory = path;
357
}
358
}
359
Platform::String^ wString = ref new Platform::String(path.ToWString().c_str());
360
StorageFolder^ storageItem;
361
ExecuteTask(storageItem, StorageFolder::GetFolderFromPathAsync(wString));
362
if (storageItem != nullptr) {
363
Platform::String^ freeSpaceKey = ref new Platform::String(L"System.FreeSpace");
364
Platform::Collections::Vector<Platform::String^>^ propertiesToRetrieve = ref new Platform::Collections::Vector<Platform::String^>();
365
propertiesToRetrieve->Append(freeSpaceKey);
366
Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ result;
367
ExecuteTask(result, storageItem->Properties->RetrievePropertiesAsync(propertiesToRetrieve));
368
if (result != nullptr && result->Size > 0) {
369
try {
370
auto value = result->Lookup(L"System.FreeSpace");
371
space = (uint64_t)value;
372
state = true;
373
}
374
catch (...) {
375
376
}
377
}
378
}
379
380
return state;
381
}
382
#pragma endregion
383
384
#pragma region Logs
385
std::string GetLogFile() {
386
std::string logFile;
387
Path logFilePath = Path(GetPSPFolder() + "\\PSP\\ppsspplog.txt");
388
HANDLE h = CreateFile2FromAppW(logFilePath.ToWString().c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, CREATE_ALWAYS, nullptr);
389
if (h != INVALID_HANDLE_VALUE) {
390
logFile = logFilePath.ToString();
391
CloseHandle(h);
392
}
393
return logFile;
394
}
395
396
#pragma endregion
397
398