git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / vfs / vfs_implementation_uwp.cpp
CommitLineData
3719602c
PC
1/* Copyright (C) 2018-2020 The RetroArch team
2*
3* ---------------------------------------------------------------------------------------
4* The following license statement only applies to this file (vfs_implementation_uwp.cpp).
5* ---------------------------------------------------------------------------------------
6*
7* Permission is hereby granted, free of charge,
8* to any person obtaining a copy of this software and associated documentation files (the "Software"),
9* to deal in the Software without restriction, including without limitation the rights to
10* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12*
13* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14*
15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21*/
22
23#include <retro_environment.h>
24
25#include <ppl.h>
26#include <ppltasks.h>
27#include <stdio.h>
28#include <wrl.h>
29#include <wrl/implements.h>
30#include <robuffer.h>
31#include <collection.h>
32#include <functional>
33#include <fileapifromapp.h>
34#include <AclAPI.h>
35#include <sddl.h>
36#include <io.h>
37#include <fcntl.h>
38
39#ifdef RARCH_INTERNAL
40#ifndef VFS_FRONTEND
41#define VFS_FRONTEND
42#endif
43#endif
44
45#include <vfs/vfs.h>
46#include <vfs/vfs_implementation.h>
47#include <libretro.h>
48#include <encodings/utf.h>
49#include <retro_miscellaneous.h>
50#include <file/file_path.h>
51#include <string/stdstring.h>
52#include <retro_environment.h>
53#include <uwp/uwp_async.h>
54#include <uwp/std_filesystem_compat.h>
55
56namespace
57{
58 /* UWP deals with paths containing / instead of
59 * \ way worse than normal Windows */
60 /* and RetroArch may sometimes mix them
61 * (e.g. on archive extraction) */
62 static void windowsize_path(wchar_t* path)
63 {
64 if (path)
65 {
66 while (*path)
67 {
68 if (*path == '/')
69 *path = '\\';
70 ++path;
71 }
72 }
73 }
74}
75
76#ifdef VFS_FRONTEND
77struct retro_vfs_file_handle
78#else
79struct libretro_vfs_implementation_file
80#endif
81{
82 int64_t size;
83 uint64_t mappos;
84 uint64_t mapsize;
85 FILE* fp;
86 HANDLE fh;
87 char* buf;
88 char* orig_path;
89 uint8_t* mapped;
90 int fd;
91 unsigned hints;
92 enum vfs_scheme scheme;
93};
94
95#define RFILE_HINT_UNBUFFERED (1 << 8)
96
97int retro_vfs_file_close_impl(libretro_vfs_implementation_file* stream)
98{
99 if (!stream)
100 return -1;
101
102 if (stream->fp)
103 fclose(stream->fp);
104
105 if (stream->buf != NULL)
106 {
107 free(stream->buf);
108 stream->buf = NULL;
109 }
110 if (stream->orig_path)
111 free(stream->orig_path);
112
113 free(stream);
114
115 return 0;
116}
117
118int retro_vfs_file_error_impl(libretro_vfs_implementation_file* stream)
119{
120 return ferror(stream->fp);
121}
122
123int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file* stream)
124{
125 if (stream)
126 return stream->size;
127 return 0;
128}
129
130int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file* stream, int64_t length)
131{
132 if (stream && _chsize(_fileno(stream->fp), length) == 0)
133 return 0;
134 return -1;
135}
136
137int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file* stream)
138{
139 if (!stream)
140 return -1;
141
142 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
143 return _ftelli64(stream->fp);
144 if (lseek(stream->fd, 0, SEEK_CUR) < 0)
145 return -1;
146
147 return 0;
148}
149
150int64_t retro_vfs_file_seek_internal(
151 libretro_vfs_implementation_file* stream,
152 int64_t offset, int whence)
153{
154 if (!stream)
155 return -1;
156
157 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
158 return _fseeki64(stream->fp, offset, whence);
159 if (lseek(stream->fd, (off_t)offset, whence) < 0)
160 return -1;
161
162 return 0;
163}
164
165int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file* stream,
166 int64_t offset, int seek_position)
167{
168 return retro_vfs_file_seek_internal(stream, offset, seek_position);
169}
170
171int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file* stream,
172 void* s, uint64_t len)
173{
174 if (!stream || (!stream->fp && stream->fh == INVALID_HANDLE_VALUE) || !s)
175 return -1;
176
177 if (stream->fh != INVALID_HANDLE_VALUE)
178 {
179 DWORD _bytes_read;
180 ReadFile(stream->fh, (char*)s, len, &_bytes_read, NULL);
181 return (int64_t)_bytes_read;
182 }
183
184 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
185 return fread(s, 1, (size_t)len, stream->fp);
186 return read(stream->fd, s, (size_t)len);
187}
188
189
190int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file* stream, const void* s, uint64_t len)
191{
192 if (!stream || (!stream->fp && stream->fh == INVALID_HANDLE_VALUE) || !s)
193 return -1;
194
195 if (stream->fh != INVALID_HANDLE_VALUE)
196 {
197 DWORD bytes_written;
198 WriteFile(stream->fh, s, len, &bytes_written, NULL);
199 return (int64_t)bytes_written;
200 }
201
202 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
203 return fwrite(s, 1, (size_t)len, stream->fp);
204
205 return write(stream->fd, s, (size_t)len);
206}
207
208int retro_vfs_file_flush_impl(libretro_vfs_implementation_file* stream)
209{
210 if (stream && fflush(stream->fp) == 0)
211 return 0;
212 return -1;
213}
214
215int retro_vfs_file_remove_impl(const char *path)
216{
217 BOOL result;
218 wchar_t *path_wide;
219
220 if (!path || !*path)
221 return -1;
222
223 path_wide = utf8_to_utf16_string_alloc(path);
224 windowsize_path(path_wide);
225
226 /* Try Win32 first, this should work in AppData */
227 result = DeleteFileFromAppW(path_wide);
228 free(path_wide);
229 if (result)
230 return 0;
231
232 return -1;
233}
234
235libretro_vfs_implementation_file* retro_vfs_file_open_impl(
236 const char* path, unsigned mode, unsigned hints)
237{
238 HANDLE file_handle;
239 std::wstring path_wstring;
240 DWORD desireAccess;
241 DWORD creationDisposition;
242 wchar_t *path_wide = NULL;
243 int flags = 0;
244 const char *mode_str = NULL;
245 libretro_vfs_implementation_file* stream =
246 (libretro_vfs_implementation_file*)
247 malloc(sizeof(*stream));
248
249 if (!stream)
250 return NULL;
251
252 stream->fd = 0;
253 stream->hints = hints;
254 stream->size = 0;
255 stream->buf = NULL;
256 stream->fp = NULL;
257 stream->fh = 0;
258 stream->orig_path = NULL;
259 stream->mappos = 0;
260 stream->mapsize = 0;
261 stream->mapped = NULL;
262 stream->scheme = VFS_SCHEME_NONE;
263
264#ifdef VFS_FRONTEND
265 if ( path
266 && path[0] == 'v'
267 && path[1] == 'f'
268 && path[2] == 's'
269 && path[3] == 'o'
270 && path[4] == 'n'
271 && path[5] == 'l'
272 && path[6] == 'y'
273 && path[7] == ':'
274 && path[8] == '/'
275 && path[9] == '/')
276 path += sizeof("vfsonly://")-1;
277#endif
278
279 path_wide = utf8_to_utf16_string_alloc(path);
280 windowsize_path(path_wide);
281 path_wstring = path_wide;
282 free(path_wide);
283
284 for (;;)
285 {
286 size_t p = path_wstring.find(L"\\\\");
287 if (p == std::wstring::npos)
288 break;
289 path_wstring.replace(p, 2, L"\\");
290 }
291
292 path_wstring = L"\\\\?\\" + path_wstring;
293 stream->orig_path = strdup(path);
294
295 stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
296
297 switch (mode)
298 {
299 case RETRO_VFS_FILE_ACCESS_READ:
300 mode_str = "rb";
301 flags = O_RDONLY | O_BINARY;
302 break;
303
304 case RETRO_VFS_FILE_ACCESS_WRITE:
305 mode_str = "wb";
306 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
307 break;
308
309 case RETRO_VFS_FILE_ACCESS_READ_WRITE:
310 mode_str = "w+b";
311 flags = O_RDWR | O_CREAT | O_TRUNC | O_BINARY;
312 break;
313
314 case RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING:
315 case RETRO_VFS_FILE_ACCESS_READ_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING:
316 mode_str = "r+b";
317 flags = O_RDWR | O_BINARY;
318 break;
319
320 default:
321 goto error;
322 }
323
324 switch (mode)
325 {
326 case RETRO_VFS_FILE_ACCESS_READ_WRITE:
327 desireAccess = GENERIC_READ | GENERIC_WRITE;
328 break;
329 case RETRO_VFS_FILE_ACCESS_WRITE:
330 desireAccess = GENERIC_WRITE;
331 break;
332 case RETRO_VFS_FILE_ACCESS_READ:
333 desireAccess = GENERIC_READ;
334 break;
335 }
336 if (mode == RETRO_VFS_FILE_ACCESS_READ)
337 creationDisposition = OPEN_EXISTING;
338 else
339 creationDisposition = (mode & RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING) != 0
340 ? OPEN_ALWAYS
341 : CREATE_ALWAYS;
342
343 if ((file_handle = CreateFile2FromAppW(path_wstring.data(), desireAccess,
344 FILE_SHARE_READ, creationDisposition, NULL)) == INVALID_HANDLE_VALUE)
345 goto error;
346
347 stream->fh = file_handle;
348 if ((stream->fd = _open_osfhandle((uint64)stream->fh, flags)) == -1)
349 goto error;
350
351 {
352 FILE *fp = _fdopen(stream->fd, mode_str);
353
354 if (!fp)
355 goto error;
356 stream->fp = fp;
357 }
358
359 /* Regarding setvbuf:
360 *
361 * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html
362 *
363 * If the size argument is not zero but buf is NULL,
364 * a buffer of the given size will be allocated immediately, and
365 * released on close. This is an extension to ANSI C.
366 *
367 * Since C89 does not support specifying a NULL buffer
368 * with a non-zero size, we create and track our own buffer for it.
369 */
370 /* TODO: this is only useful for a few platforms,
371 * find which and add ifdef */
372 if (stream->scheme != VFS_SCHEME_CDROM)
373 {
374 stream->buf = (char*)calloc(1, 0x4000);
375 if (stream->fp)
376 setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
377 }
378
379 retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
380 retro_vfs_file_seek_internal(stream, 0, SEEK_END);
381
382 stream->size = retro_vfs_file_tell_impl(stream);
383
384 retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
385
386 return stream;
387
388error:
389 retro_vfs_file_close_impl(stream);
390 return NULL;
391}
392
393static int uwp_mkdir_impl(std::experimental::filesystem::path dir)
394{
395 /*I feel like this should create the directory recursively but the existing implementation does not so this update won't
396 *I put in the work but I just commented out the stuff you would need */
397 WIN32_FILE_ATTRIBUTE_DATA lpFileInfo;
398 bool parent_dir_exists = false;
399
400 if (dir.empty())
401 return -1;
402
403 /* Check if file attributes can be gotten successfully */
404 if (GetFileAttributesExFromAppW(dir.parent_path().wstring().c_str(), GetFileExInfoStandard, &lpFileInfo))
405 {
406 /* Check that the files attributes are not null or empty */
407 if (lpFileInfo.dwFileAttributes != INVALID_FILE_ATTRIBUTES && lpFileInfo.dwFileAttributes != 0)
408 {
409 if (lpFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
410 parent_dir_exists = true;
411 }
412 }
413 if (!parent_dir_exists)
414 {
415 /* Try to create parent dir */
416 int success = uwp_mkdir_impl(dir.parent_path());
417 if (success != 0 && success != -2)
418 return success;
419 }
420
421
422 /* Try Win32 first, this should work in AppData */
423 if (CreateDirectoryFromAppW(dir.wstring().c_str(), NULL))
424 return 0;
425
426 if (GetLastError() == ERROR_ALREADY_EXISTS)
427 return -2;
428
429 return -1;
430}
431
432int retro_vfs_mkdir_impl(const char* dir)
433{
434 return uwp_mkdir_impl(std::filesystem::path(dir));
435}
436
437/* The first run paramater is used to avoid error checking
438 * when doing recursion.
439 * Unlike the initial implementation, this can move folders
440 * even empty ones when you want to move a directory structure.
441 *
442 * This will fail even if a single file cannot be moved.
443 */
444static int uwp_move_path(
445 std::filesystem::path old_path,
446 std::filesystem::path new_path,
447 bool firstrun)
448{
449 if (old_path.empty() || new_path.empty())
450 return -1;
451
452 if (firstrun)
453 {
454 WIN32_FILE_ATTRIBUTE_DATA lpFileInfo, targetfileinfo;
455 bool parent_dir_exists = false;
456
457 /* Make sure that parent path exists */
458 if (GetFileAttributesExFromAppW(
459 new_path.parent_path().wstring().c_str(),
460 GetFileExInfoStandard, &lpFileInfo))
461 {
462 /* Check that the files attributes are not null or empty */
463 if ( lpFileInfo.dwFileAttributes
464 != INVALID_FILE_ATTRIBUTES
465 && lpFileInfo.dwFileAttributes != 0)
466 {
467 /* Parent path doesn't exist, so we gotta create it */
468 if (!(lpFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
469 uwp_mkdir_impl(new_path.parent_path());
470 }
471 }
472
473 /* Make sure that source path exists */
474 if (GetFileAttributesExFromAppW(old_path.wstring().c_str(), GetFileExInfoStandard, &lpFileInfo))
475 {
476 /* Check that the files attributes are not null or empty */
477 if ( lpFileInfo.dwFileAttributes != INVALID_FILE_ATTRIBUTES
478 && lpFileInfo.dwFileAttributes != 0)
479 {
480 /* Check if source path is a dir */
481 if (lpFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
482 {
483 int result;
484 /* create the target dir */
485 CreateDirectoryFromAppW(new_path.wstring().c_str(), NULL);
486 /* Call move function again but with first run disabled in
487 * order to move the folder */
488 if ((result = uwp_move_path(old_path, new_path, false)) != 0)
489 return result;
490 }
491 else
492 {
493 /* The file that we want to move exists so we can copy it now
494 * check if target file already exists. */
495 if (GetFileAttributesExFromAppW(
496 new_path.wstring().c_str(),
497 GetFileExInfoStandard,
498 &targetfileinfo))
499 {
500 if (
501 targetfileinfo.dwFileAttributes != INVALID_FILE_ATTRIBUTES
502 && targetfileinfo.dwFileAttributes != 0
503 && (!(targetfileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))
504 {
505 if (DeleteFileFromAppW(new_path.wstring().c_str()))
506 return -1;
507 }
508 }
509
510 if (!MoveFileFromAppW(old_path.wstring().c_str(),
511 new_path.wstring().c_str()))
512 return -1;
513 /* Set ACL */
514 uwp_set_acl(new_path.wstring().c_str(), L"S-1-15-2-1");
515 }
516 }
517 }
518
519 }
520 else
521 {
522 HANDLE searchResults;
523 WIN32_FIND_DATA findDataResult;
524 /* We are bypassing error checking and moving a dir.
525 * First we have to get a list of files in the dir. */
526 wchar_t* filteredPath = wcsdup(old_path.wstring().c_str());
527 wcscat_s(filteredPath, sizeof(L"\\*.*"), L"\\*.*");
528 searchResults = FindFirstFileExFromAppW(
529 filteredPath, FindExInfoBasic, &findDataResult,
530 FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
531
532 if (searchResults != INVALID_HANDLE_VALUE)
533 {
534 bool fail = false;
535 do
536 {
537 if ( wcscmp(findDataResult.cFileName, L".") != 0
538 && wcscmp(findDataResult.cFileName, L"..") != 0)
539 {
540 std::filesystem::path temp_old = old_path;
541 std::filesystem::path temp_new = new_path;
542 temp_old /= findDataResult.cFileName;
543 temp_new /= findDataResult.cFileName;
544 if ( findDataResult.dwFileAttributes
545 & FILE_ATTRIBUTE_DIRECTORY)
546 {
547 CreateDirectoryFromAppW(temp_new.wstring().c_str(), NULL);
548 if (uwp_move_path(temp_old, temp_new, false) != 0)
549 fail = true;
550 }
551 else
552 {
553 WIN32_FILE_ATTRIBUTE_DATA targetfileinfo;
554 /* The file that we want to move exists so we can copy
555 * it now.
556 * Check if target file already exists. */
557 if (GetFileAttributesExFromAppW(temp_new.wstring().c_str(),
558 GetFileExInfoStandard, &targetfileinfo))
559 {
560 if (
561 (targetfileinfo.dwFileAttributes !=
562 INVALID_FILE_ATTRIBUTES)
563 && (targetfileinfo.dwFileAttributes != 0)
564 && (!(targetfileinfo.dwFileAttributes
565 & FILE_ATTRIBUTE_DIRECTORY)))
566 {
567 if (DeleteFileFromAppW(temp_new.wstring().c_str()))
568 fail = true;
569 }
570 }
571
572 if (!MoveFileFromAppW(temp_old.wstring().c_str(),
573 temp_new.wstring().c_str()))
574 fail = true;
575 /* Set ACL - this step sucks or at least used to
576 * before I made a whole function
577 * Don't know if we actually "need" to set the ACL
578 * though */
579 uwp_set_acl(temp_new.wstring().c_str(), L"S-1-15-2-1");
580 }
581 }
582 } while (FindNextFile(searchResults, &findDataResult));
583 FindClose(searchResults);
584 if (fail)
585 return -1;
586 }
587 free(filteredPath);
588 }
589 return 0;
590}
591
592/* C doesn't support default arguments so we wrap it up in a shell to enable
593 * us to use default arguments.
594 * Default arguments mean that we can do better recursion */
595int retro_vfs_file_rename_impl(const char* old_path, const char* new_path)
596{
597 return uwp_move_path(std::filesystem::path(old_path),
598 std::filesystem::path(old_path), true);
599}
600
601const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *stream)
602{
603 /* Should never happen, do something noisy so caller can be fixed */
604 if (!stream)
605 abort();
606 return stream->orig_path;
607}
608
609int retro_vfs_stat_impl(const char *path, int32_t *size)
610{
611 wchar_t *path_wide;
612 _WIN32_FILE_ATTRIBUTE_DATA attribdata;
613
614 if (!path || !*path)
615 return 0;
616
617 path_wide = utf8_to_utf16_string_alloc(path);
618 windowsize_path(path_wide);
619
620 /* Try Win32 first, this should work in AppData */
621 if (GetFileAttributesExFromAppW(path_wide,
622 GetFileExInfoStandard, &attribdata))
623 {
624 if (attribdata.dwFileAttributes != INVALID_FILE_ATTRIBUTES)
625 {
626 if (!(attribdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
627 {
628 LARGE_INTEGER sz;
629 if (size)
630 {
631 sz.HighPart = attribdata.nFileSizeHigh;
632 sz.LowPart = attribdata.nFileSizeLow;
633 *size = sz.QuadPart;
634 }
635 }
636 free(path_wide);
637 return (attribdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
638 ? RETRO_VFS_STAT_IS_VALID | RETRO_VFS_STAT_IS_DIRECTORY
639 : RETRO_VFS_STAT_IS_VALID;
640 }
641 }
642 free(path_wide);
643 return 0;
644}
645
646#ifdef VFS_FRONTEND
647struct retro_vfs_dir_handle
648#else
649struct libretro_vfs_implementation_dir
650#endif
651{
652 char* orig_path;
653 WIN32_FIND_DATAW entry;
654 HANDLE directory;
655 bool next;
656 char path[PATH_MAX_LENGTH];
657};
658
659libretro_vfs_implementation_dir* retro_vfs_opendir_impl(
660 const char* name, bool include_hidden)
661{
662 char path_buf[1024];
663 size_t copied = 0;
664 wchar_t* path_wide = NULL;
665 libretro_vfs_implementation_dir* rdir;
666
667 /* Reject NULL or empty string paths*/
668 if (!name || (*name == 0))
669 return NULL;
670
671 /*Allocate RDIR struct. Tidied later with retro_closedir*/
672 if (!(rdir = (libretro_vfs_implementation_dir*)calloc(1, sizeof(*rdir))))
673 return NULL;
674
675 rdir->orig_path = strdup(name);
676
677 copied = strlcpy(path_buf, name, sizeof(path_buf));
678
679 /* Non-NT platforms don't like extra slashes in the path */
680 if (path_buf[copied - 1] != '\\')
681 path_buf[copied++] = '\\';
682
683 path_buf[copied] = '*';
684 path_buf[copied + 1] = '\0';
685
686 path_wide = utf8_to_utf16_string_alloc(path_buf);
687 rdir->directory = FindFirstFileExFromAppW(
688 path_wide, FindExInfoStandard, &rdir->entry,
689 FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
690
691 if (path_wide)
692 free(path_wide);
693
694 if (include_hidden)
695 rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
696 else
697 rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
698
699 if (rdir->directory && rdir != INVALID_HANDLE_VALUE)
700 return rdir;
701
702 retro_vfs_closedir_impl(rdir);
703 return NULL;
704}
705
706bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir* rdir)
707{
708 if (rdir->next)
709 return (FindNextFileW(rdir->directory, &rdir->entry) != 0);
710
711 rdir->next = true;
712 return (rdir->directory != INVALID_HANDLE_VALUE);
713}
714
715const char* retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir* rdir)
716{
717 char* name = utf16_to_utf8_string_alloc(rdir->entry.cFileName);
718 memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
719 strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName));
720 if (name)
721 free(name);
722 return (char*)rdir->entry.cFileName;
723}
724
725bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir* rdir)
726{
727 const WIN32_FIND_DATA* entry = (const WIN32_FIND_DATA*)&rdir->entry;
728 return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
729}
730
731int retro_vfs_closedir_impl(libretro_vfs_implementation_dir* rdir)
732{
733 if (!rdir)
734 return -1;
735
736 if (rdir->directory != INVALID_HANDLE_VALUE)
737 FindClose(rdir->directory);
738
739 if (rdir->orig_path)
740 free(rdir->orig_path);
741 free(rdir);
742 return 0;
743}
744
745void uwp_set_acl(const wchar_t* path, const wchar_t* AccessString)
746{
747 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
748 EXPLICIT_ACCESSW ExplicitAccess = { 0 };
749 ACL* AccessControlCurrent = NULL;
750 ACL* AccessControlNew = NULL;
751 SECURITY_INFORMATION SecurityInfo = DACL_SECURITY_INFORMATION;
752 PSID SecurityIdentifier = NULL;
753 HANDLE original_file = CreateFileFromAppW(path,
754 GENERIC_READ | GENERIC_WRITE | WRITE_DAC,
755 FILE_SHARE_READ | FILE_SHARE_WRITE,
756 NULL, OPEN_EXISTING, 0, NULL);
757
758 if (original_file != INVALID_HANDLE_VALUE)
759 {
760 if (
761 GetSecurityInfo(
762 original_file,
763 SE_FILE_OBJECT,
764 DACL_SECURITY_INFORMATION,
765 NULL,
766 NULL,
767 &AccessControlCurrent,
768 NULL,
769 &SecurityDescriptor
770 ) == ERROR_SUCCESS
771 )
772 {
773 ConvertStringSidToSidW(AccessString, &SecurityIdentifier);
774 if (SecurityIdentifier)
775 {
776 ExplicitAccess.grfAccessPermissions= GENERIC_READ | GENERIC_EXECUTE | GENERIC_WRITE;
777 ExplicitAccess.grfAccessMode = SET_ACCESS;
778 ExplicitAccess.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
779 ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID;
780 ExplicitAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
781 ExplicitAccess.Trustee.ptstrName = reinterpret_cast<wchar_t*>(SecurityIdentifier);
782
783 if (
784 SetEntriesInAclW(
785 1,
786 &ExplicitAccess,
787 AccessControlCurrent,
788 &AccessControlNew
789 ) == ERROR_SUCCESS
790 )
791 SetSecurityInfo(
792 original_file,
793 SE_FILE_OBJECT,
794 SecurityInfo,
795 NULL,
796 NULL,
797 AccessControlNew,
798 NULL
799 );
800 }
801 }
802 if (SecurityDescriptor)
803 LocalFree(reinterpret_cast<HLOCAL>(SecurityDescriptor));
804 if (AccessControlNew)
805 LocalFree(reinterpret_cast<HLOCAL>(AccessControlNew));
806 CloseHandle(original_file);
807 }
808}