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