2020-01-31 15:43:42 +01:00
/* Copyright (C) 2018-2020 The RetroArch team
2019-01-06 14:49:26 +01:00
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* The following license statement only applies to this file ( vfs_implementation_uwp . cpp ) .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Permission is hereby granted , free of charge ,
* to any person obtaining a copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation the rights to
* use , copy , modify , merge , publish , distribute , sublicense , and / or sell copies of the Software ,
* and to permit persons to whom the Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR IMPLIED ,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY ,
* WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*/
2019-07-06 07:29:51 +02:00
# include <retro_environment.h>
2019-01-06 14:49:26 +01:00
# include <ppl.h>
# include <ppltasks.h>
# include <stdio.h>
# include <wrl.h>
# include <wrl/implements.h>
# include <windows.storage.streams.h>
# include <robuffer.h>
2019-01-14 22:43:29 +01:00
# include <collection.h>
2019-01-06 14:49:26 +01:00
# include <functional>
2021-07-31 04:36:08 +01:00
# include <fileapifromapp.h>
2021-08-25 15:54:09 +01:00
# include <AclAPI.h>
2019-01-06 14:49:26 +01:00
2019-01-14 22:43:29 +01:00
using namespace Windows : : Foundation ;
2019-01-06 14:49:26 +01:00
using namespace Windows : : Foundation : : Collections ;
using namespace Windows : : Storage ;
using namespace Windows : : Storage : : Streams ;
using namespace Windows : : Storage : : FileProperties ;
# ifdef RARCH_INTERNAL
# ifndef VFS_FRONTEND
# define VFS_FRONTEND
# endif
# endif
2019-07-05 23:45:48 -04:00
# include <vfs/vfs.h>
2019-01-06 14:49:26 +01:00
# include <vfs/vfs_implementation.h>
# include <libretro.h>
# include <encodings/utf.h>
# include <retro_miscellaneous.h>
# include <file/file_path.h>
# include <retro_assert.h>
# include <string/stdstring.h>
# include <retro_environment.h>
2019-12-05 15:27:08 +08:00
# include <uwp/uwp_async.h>
2021-01-07 00:13:02 +08:00
# include <uwp/uwp_file_handle_access.h>
2021-08-25 15:54:09 +01:00
# include <uwp/std_filesystem_compat.h>
// define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING to silence warnings
// idk why this warning happens considering we can't use the non experimental version but whatever ig
2019-01-06 14:49:26 +01:00
namespace
{
2021-01-07 00:13:02 +08:00
void windowsize_path ( wchar_t * path )
{
/* UWP deals with paths containing / instead of
2020-06-25 14:01:19 +02:00
* \ way worse than normal Windows */
2021-01-07 00:13:02 +08:00
/* and RetroArch may sometimes mix them
2020-06-25 14:01:19 +02:00
* ( e . g . on archive extraction ) */
2021-01-07 00:13:02 +08:00
if ( ! path )
return ;
while ( * path )
{
if ( * path = = ' / ' )
* path = ' \\ ' ;
+ + path ;
}
}
2019-01-06 14:49:26 +01:00
}
2019-01-14 22:43:29 +01:00
namespace
{
2021-01-07 00:13:02 +08:00
/* Damn you, UWP, why no functions for that either */
template < typename T >
concurrency : : task < T ^ > GetItemFromPathAsync ( Platform : : String ^ path )
{
static_assert ( false , " StorageFile and StorageFolder only " ) ;
}
template < >
concurrency : : task < StorageFile ^ > GetItemFromPathAsync ( Platform : : String ^ path )
{
return concurrency : : create_task ( StorageFile : : GetFileFromPathAsync ( path ) ) ;
}
template < >
concurrency : : task < StorageFolder ^ > GetItemFromPathAsync ( Platform : : String ^ path )
{
return concurrency : : create_task ( StorageFolder : : GetFolderFromPathAsync ( path ) ) ;
}
template < typename T >
concurrency : : task < T ^ > GetItemInFolderFromPathAsync ( StorageFolder ^ folder , Platform : : String ^ path )
{
static_assert ( false , " StorageFile and StorageFolder only " ) ;
}
template < >
concurrency : : task < StorageFile ^ > GetItemInFolderFromPathAsync ( StorageFolder ^ folder , Platform : : String ^ path )
{
if ( path - > IsEmpty ( ) )
retro_assert ( false ) ; /* Attempt to read a folder as a file - this really should have been caught earlier */
return concurrency : : create_task ( folder - > GetFileAsync ( path ) ) ;
}
template < >
concurrency : : task < StorageFolder ^ > GetItemInFolderFromPathAsync ( StorageFolder ^ folder , Platform : : String ^ path )
{
if ( path - > IsEmpty ( ) )
return concurrency : : create_task ( concurrency : : create_async ( [ folder ] ( ) { return folder ; } ) ) ;
return concurrency : : create_task ( folder - > GetFolderAsync ( path ) ) ;
}
2019-01-14 22:43:29 +01:00
}
namespace
{
2021-01-07 00:13:02 +08:00
/* A list of all StorageFolder objects returned using from the file picker */
Platform : : Collections : : Vector < StorageFolder ^ > accessible_directories ;
concurrency : : task < Platform : : String ^ > TriggerPickerAddDialog ( )
{
auto folderPicker = ref new Windows : : Storage : : Pickers : : FolderPicker ( ) ;
folderPicker - > SuggestedStartLocation = Windows : : Storage : : Pickers : : PickerLocationId : : Desktop ;
folderPicker - > FileTypeFilter - > Append ( " * " ) ;
return concurrency : : create_task ( folderPicker - > PickSingleFolderAsync ( ) ) . then ( [ ] ( StorageFolder ^ folder ) {
if ( folder = = nullptr )
throw ref new Platform : : Exception ( E_ABORT , L " Operation cancelled by user " ) ;
/* TODO: check for duplicates */
accessible_directories . Append ( folder ) ;
return folder - > Path ;
} ) ;
}
template < typename T >
concurrency : : task < T ^ > LocateStorageItem ( Platform : : String ^ path )
{
/* Look for a matching directory we can use */
for each ( StorageFolder ^ folder in accessible_directories )
{
2020-06-25 14:01:19 +02:00
std : : wstring file_path ;
2021-01-07 00:13:02 +08:00
std : : wstring folder_path = folder - > Path - > Data ( ) ;
2020-06-25 14:01:19 +02:00
size_t folder_path_size = folder_path . size ( ) ;
2021-01-07 00:13:02 +08:00
/* Could be C:\ or C:\Users\somebody - remove the trailing slash to unify them */
if ( folder_path [ folder_path_size - 1 ] = = ' \\ ' )
folder_path . erase ( folder_path_size - 1 ) ;
2020-06-25 14:01:19 +02:00
2021-01-07 00:13:02 +08:00
file_path = path - > Data ( ) ;
2020-06-25 14:01:19 +02:00
2021-01-07 00:13:02 +08:00
if ( file_path . find ( folder_path ) = = 0 )
{
/* Found a match */
file_path = file_path . length ( ) > folder_path . length ( )
2020-06-25 14:01:19 +02:00
? file_path . substr ( folder_path . length ( ) + 1 )
: L " " ;
2021-01-07 00:13:02 +08:00
return concurrency : : create_task ( GetItemInFolderFromPathAsync < T > ( folder , ref new Platform : : String ( file_path . data ( ) ) ) ) ;
}
}
/* No matches - try accessing directly, and fallback to user prompt */
return concurrency : : create_task ( GetItemFromPathAsync < T > ( path ) ) . then ( [ & ] ( concurrency : : task < T ^ > item ) {
try
{
T ^ storageItem = item . get ( ) ;
return concurrency : : create_task ( concurrency : : create_async ( [ storageItem ] ( ) { return storageItem ; } ) ) ;
}
catch ( Platform : : AccessDeniedException ^ e )
{
2021-09-06 13:27:49 +01:00
//for some reason the path is inaccessible from within here???
Windows : : UI : : Popups : : MessageDialog ^ dialog = ref new Windows : : UI : : Popups : : MessageDialog ( " Path is not currently accessible. Please open any containing directory to access it. " ) ;
2021-01-07 00:13:02 +08:00
dialog - > Commands - > Append ( ref new Windows : : UI : : Popups : : UICommand ( " Open file picker " ) ) ;
dialog - > Commands - > Append ( ref new Windows : : UI : : Popups : : UICommand ( " Cancel " ) ) ;
return concurrency : : create_task ( dialog - > ShowAsync ( ) ) . then ( [ path ] ( Windows : : UI : : Popups : : IUICommand ^ cmd ) {
if ( cmd - > Label = = " Open file picker " )
{
return TriggerPickerAddDialog ( ) . then ( [ path ] ( Platform : : String ^ added_path ) {
2021-09-06 13:27:49 +01:00
// Retry
2021-01-07 00:13:02 +08:00
return LocateStorageItem < T > ( path ) ;
} ) ;
}
else
{
throw ref new Platform : : Exception ( E_ABORT , L " Operation cancelled by user " ) ;
}
} ) ;
}
} ) ;
}
IStorageItem ^ LocateStorageFileOrFolder ( Platform : : String ^ path )
{
if ( ! path | | path - > IsEmpty ( ) )
return nullptr ;
if ( * ( path - > End ( ) - 1 ) = = ' \\ ' )
{
/* Ends with a slash, so it's definitely a directory */
return RunAsyncAndCatchErrors < StorageFolder ^ > ( [ & ] ( ) {
return concurrency : : create_task ( LocateStorageItem < StorageFolder > ( path ) ) ;
} , nullptr ) ;
}
else
{
/* No final slash - probably a file (since RetroArch usually slash-terminates dirs), but there is still a chance it's a directory */
IStorageItem ^ item ;
item = RunAsyncAndCatchErrors < StorageFile ^ > ( [ & ] ( ) {
return concurrency : : create_task ( LocateStorageItem < StorageFile > ( path ) ) ;
} , nullptr ) ;
if ( ! item )
{
item = RunAsyncAndCatchErrors < StorageFolder ^ > ( [ & ] ( ) {
return concurrency : : create_task ( LocateStorageItem < StorageFolder > ( path ) ) ;
} , nullptr ) ;
}
return item ;
}
}
2019-01-14 22:43:29 +01:00
}
2019-04-27 23:47:52 +02:00
/* This is some pure magic and I have absolutely no idea how it works */
/* Wraps a raw buffer into a WinRT object */
/* https://stackoverflow.com/questions/10520335/how-to-wrap-a-char-buffer-in-a-winrt-ibuffer-in-c */
class NativeBuffer :
2021-01-07 00:13:02 +08:00
public Microsoft : : WRL : : RuntimeClass < Microsoft : : WRL : : RuntimeClassFlags < Microsoft : : WRL : : RuntimeClassType : : WinRtClassicComMix > ,
ABI : : Windows : : Storage : : Streams : : IBuffer ,
Windows : : Storage : : Streams : : IBufferByteAccess >
2019-04-27 23:47:52 +02:00
{
public :
2021-01-07 00:13:02 +08:00
virtual ~ NativeBuffer ( )
{
}
2019-04-27 23:47:52 +02:00
2021-01-07 00:13:02 +08:00
HRESULT __stdcall RuntimeClassInitialize (
2020-06-25 14:01:19 +02:00
byte * buffer , uint32_t capacity , uint32_t length )
2021-01-07 00:13:02 +08:00
{
m_buffer = buffer ;
m_capacity = capacity ;
m_length = length ;
return S_OK ;
}
HRESULT __stdcall Buffer ( byte * * value )
{
if ( m_buffer = = nullptr )
return E_INVALIDARG ;
* value = m_buffer ;
return S_OK ;
}
HRESULT __stdcall get_Capacity ( uint32_t * value )
{
* value = m_capacity ;
return S_OK ;
}
HRESULT __stdcall get_Length ( uint32_t * value )
{
* value = m_length ;
return S_OK ;
}
HRESULT __stdcall put_Length ( uint32_t value )
{
if ( value > m_capacity )
return E_INVALIDARG ;
m_length = value ;
return S_OK ;
}
2019-04-27 23:47:52 +02:00
private :
2021-01-07 00:13:02 +08:00
byte * m_buffer ;
uint32_t m_capacity ;
uint32_t m_length ;
2019-04-27 23:47:52 +02:00
} ;
IBuffer ^ CreateNativeBuffer ( void * buf , uint32_t capacity , uint32_t length )
{
2021-01-07 00:13:02 +08:00
Microsoft : : WRL : : ComPtr < NativeBuffer > nativeBuffer ;
Microsoft : : WRL : : Details : : MakeAndInitialize < NativeBuffer > ( & nativeBuffer , ( byte * ) buf , capacity , length ) ;
auto iinspectable = ( IInspectable * ) reinterpret_cast < IInspectable * > ( nativeBuffer . Get ( ) ) ;
IBuffer ^ buffer = reinterpret_cast < IBuffer ^ > ( iinspectable ) ;
return buffer ;
}
/* Get a Win32 file handle out of IStorageFile */
/* https://stackoverflow.com/questions/42799235/how-can-i-get-a-win32-handle-for-a-storagefile-or-storagefolder-in-uwp */
HRESULT GetHandleFromStorageFile ( Windows : : Storage : : StorageFile ^ file , HANDLE * handle , HANDLE_ACCESS_OPTIONS accessMode )
{
Microsoft : : WRL : : ComPtr < IUnknown > abiPointer ( reinterpret_cast < IUnknown * > ( file ) ) ;
Microsoft : : WRL : : ComPtr < IStorageItemHandleAccess > handleAccess ;
2021-01-16 17:30:36 +01:00
if ( SUCCEEDED ( abiPointer . As ( & handleAccess ) ) )
{
2021-01-07 00:13:02 +08:00
HANDLE hFile = INVALID_HANDLE_VALUE ;
if ( SUCCEEDED ( handleAccess - > Create ( accessMode ,
2021-01-16 17:30:36 +01:00
HANDLE_SHARING_OPTIONS : : HSO_SHARE_READ ,
HANDLE_OPTIONS : : HO_NONE ,
nullptr ,
& hFile ) ) )
{
2021-01-07 00:13:02 +08:00
* handle = hFile ;
return S_OK ;
}
}
return E_FAIL ;
2019-04-27 23:47:52 +02:00
}
2019-07-04 04:40:13 +02:00
# ifdef VFS_FRONTEND
struct retro_vfs_file_handle
# else
struct libretro_vfs_implementation_file
# endif
{
2021-01-07 00:13:02 +08:00
IRandomAccessStream ^ fp ;
IBuffer ^ bufferp ;
HANDLE file_handle ;
char * buffer ;
char * orig_path ;
size_t buffer_size ;
int buffer_left ;
size_t buffer_fill ;
2019-07-04 04:40:13 +02:00
} ;
2020-06-25 14:01:19 +02:00
libretro_vfs_implementation_file * retro_vfs_file_open_impl (
const char * path , unsigned mode , unsigned hints )
2019-01-06 14:49:26 +01:00
{
2020-08-18 14:26:15 +02:00
char dirpath [ PATH_MAX_LENGTH ] ;
char filename [ PATH_MAX_LENGTH ] ;
2021-01-07 00:13:02 +08:00
wchar_t * path_wide ;
2020-06-25 14:01:19 +02:00
wchar_t * dirpath_wide ;
wchar_t * filename_wide ;
2021-01-07 00:13:02 +08:00
Platform : : String ^ path_str ;
2020-06-25 14:01:19 +02:00
Platform : : String ^ filename_str ;
Platform : : String ^ dirpath_str ;
2021-01-07 00:13:02 +08:00
HANDLE file_handle = INVALID_HANDLE_VALUE ;
DWORD desireAccess ;
DWORD creationDisposition ;
if ( ! path | | ! * path )
return NULL ;
2019-01-06 14:49:26 +01:00
2020-06-25 14:01:19 +02:00
/* Something tried to access files from current directory.
* This is not allowed on UWP . */
2021-01-07 00:13:02 +08:00
if ( ! path_is_absolute ( path ) )
return NULL ;
2019-01-06 14:49:26 +01:00
2020-01-08 00:28:30 +01:00
/* Trying to open a directory as file?! */
2021-01-07 00:13:02 +08:00
if ( PATH_CHAR_IS_SLASH ( path [ strlen ( path ) - 1 ] ) )
return NULL ;
2019-01-06 14:49:26 +01:00
2020-08-18 14:26:15 +02:00
dirpath [ 0 ] = filename [ 0 ] = ' \0 ' ;
2021-01-07 00:13:02 +08:00
path_wide = utf8_to_utf16_string_alloc ( path ) ;
windowsize_path ( path_wide ) ;
2021-09-15 20:44:58 +01:00
std : : wstring temp_path = path_wide ;
while ( true ) {
size_t p = temp_path . find ( L " \\ \\ " ) ;
if ( p = = std : : wstring : : npos ) break ;
temp_path . replace ( p , 2 , L " \\ " ) ;
}
path_wide = _wcsdup ( temp_path . c_str ( ) ) ;
2021-01-07 00:13:02 +08:00
path_str = ref new Platform : : String ( path_wide ) ;
free ( path_wide ) ;
fill_pathname_basedir ( dirpath , path , sizeof ( dirpath ) ) ;
dirpath_wide = utf8_to_utf16_string_alloc ( dirpath ) ;
windowsize_path ( dirpath_wide ) ;
dirpath_str = ref new Platform : : String ( dirpath_wide ) ;
free ( dirpath_wide ) ;
fill_pathname_base ( filename , path , sizeof ( filename ) ) ;
filename_wide = utf8_to_utf16_string_alloc ( filename ) ;
filename_str = ref new Platform : : String ( filename_wide ) ;
free ( filename_wide ) ;
retro_assert ( ! dirpath_str - > IsEmpty ( ) & & ! filename_str - > IsEmpty ( ) ) ;
/* Try Win32 first, this should work in AppData */
2021-08-25 23:12:29 +01:00
switch ( mode )
{
2021-08-26 23:18:42 +01:00
case RETRO_VFS_FILE_ACCESS_READ_WRITE :
desireAccess = GENERIC_READ | GENERIC_WRITE ;
2021-08-25 23:12:29 +01:00
break ;
case RETRO_VFS_FILE_ACCESS_WRITE :
2021-08-26 23:18:42 +01:00
desireAccess = GENERIC_WRITE ;
2021-08-25 23:12:29 +01:00
break ;
2021-08-26 23:18:42 +01:00
case RETRO_VFS_FILE_ACCESS_READ :
desireAccess = GENERIC_READ ;
2021-08-25 23:12:29 +01:00
break ;
}
2021-01-16 17:30:36 +01:00
if ( mode = = RETRO_VFS_FILE_ACCESS_READ )
{
2021-08-10 17:24:31 +01:00
creationDisposition = OPEN_EXISTING ;
2021-01-07 00:13:02 +08:00
}
2021-01-16 17:30:36 +01:00
else
{
2021-01-07 00:13:02 +08:00
creationDisposition = ( mode & RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING ) ! = 0 ?
2021-08-26 23:18:42 +01:00
OPEN_ALWAYS : CREATE_ALWAYS ;
2021-01-07 00:13:02 +08:00
}
2021-09-12 20:26:09 +01:00
path_str = " \\ \\ ? \\ " + path_str ;
2021-07-31 04:36:08 +01:00
file_handle = CreateFile2FromAppW ( path_str - > Data ( ) , desireAccess , FILE_SHARE_READ , creationDisposition , NULL ) ;
2021-01-16 17:30:36 +01:00
if ( file_handle ! = INVALID_HANDLE_VALUE )
{
2021-01-07 00:13:02 +08:00
libretro_vfs_implementation_file * stream = ( libretro_vfs_implementation_file * ) calloc ( 1 , sizeof ( * stream ) ) ;
if ( ! stream )
return ( libretro_vfs_implementation_file * ) NULL ;
stream - > orig_path = strdup ( path ) ;
stream - > fp = nullptr ;
stream - > file_handle = file_handle ;
stream - > buffer_left = 0 ;
stream - > buffer_fill = 0 ;
return stream ;
}
2021-08-25 23:12:29 +01:00
return NULL ;
2019-01-06 14:49:26 +01:00
}
int retro_vfs_file_close_impl ( libretro_vfs_implementation_file * stream )
{
2021-01-07 00:13:02 +08:00
if ( ! stream | | ( ! stream - > fp & & stream - > file_handle = = INVALID_HANDLE_VALUE ) )
return - 1 ;
2019-01-06 14:49:26 +01:00
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
2021-01-07 00:13:02 +08:00
CloseHandle ( stream - > file_handle ) ;
2021-01-16 17:30:36 +01:00
else
{
2021-01-07 00:13:02 +08:00
/* Apparently, this is how you close a file in WinRT */
/* Yes, really */
stream - > fp = nullptr ;
free ( stream - > buffer ) ;
}
2019-01-06 14:49:26 +01:00
2021-01-07 00:13:02 +08:00
return 0 ;
2019-01-06 14:49:26 +01:00
}
int retro_vfs_file_error_impl ( libretro_vfs_implementation_file * stream )
{
2021-01-07 00:13:02 +08:00
return false ; /* TODO */
2019-01-06 14:49:26 +01:00
}
int64_t retro_vfs_file_size_impl ( libretro_vfs_implementation_file * stream )
{
2021-01-07 00:13:02 +08:00
if ( ! stream | | ( ! stream - > fp & & stream - > file_handle = = INVALID_HANDLE_VALUE ) )
return 0 ;
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
{
2021-01-07 00:13:02 +08:00
LARGE_INTEGER sz ;
2021-01-16 17:30:36 +01:00
if ( GetFileSizeEx ( stream - > file_handle , & sz ) )
2021-01-07 00:13:02 +08:00
return sz . QuadPart ;
2021-01-16 17:30:36 +01:00
return 0 ;
2021-01-07 00:13:02 +08:00
}
2021-01-16 17:30:36 +01:00
return stream - > fp - > Size ;
2019-01-06 14:49:26 +01:00
}
int64_t retro_vfs_file_truncate_impl ( libretro_vfs_implementation_file * stream , int64_t length )
{
2021-01-07 00:13:02 +08:00
if ( ! stream | | ( ! stream - > fp & & stream - > file_handle = = INVALID_HANDLE_VALUE ) )
return - 1 ;
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
{
2021-01-07 00:13:02 +08:00
int64_t p = retro_vfs_file_tell_impl ( stream ) ;
retro_vfs_file_seek_impl ( stream , length , RETRO_VFS_SEEK_POSITION_START ) ;
SetEndOfFile ( stream - > file_handle ) ;
2021-01-16 17:30:36 +01:00
if ( p < length )
2021-01-07 00:13:02 +08:00
retro_vfs_file_seek_impl ( stream , p , RETRO_VFS_SEEK_POSITION_START ) ;
}
2021-01-16 17:30:36 +01:00
else
stream - > fp - > Size = length ;
2021-01-07 00:13:02 +08:00
return 0 ;
2019-01-06 14:49:26 +01:00
}
int64_t retro_vfs_file_tell_impl ( libretro_vfs_implementation_file * stream )
{
2021-01-07 00:13:02 +08:00
LARGE_INTEGER _offset ;
LARGE_INTEGER out ;
_offset . QuadPart = 0 ;
if ( ! stream | | ( ! stream - > fp & & stream - > file_handle = = INVALID_HANDLE_VALUE ) )
return - 1 ;
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
{
2021-01-07 00:13:02 +08:00
SetFilePointerEx ( stream - > file_handle , _offset , & out , FILE_CURRENT ) ;
return out . QuadPart ;
}
2021-01-16 17:30:36 +01:00
return stream - > fp - > Position - stream - > buffer_left ;
2019-01-06 14:49:26 +01:00
}
2020-06-25 14:01:19 +02:00
int64_t retro_vfs_file_seek_impl (
libretro_vfs_implementation_file * stream ,
int64_t offset , int seek_position )
2019-01-06 14:49:26 +01:00
{
2021-01-07 00:13:02 +08:00
LARGE_INTEGER _offset ;
_offset . QuadPart = offset ;
if ( ! stream | | ( ! stream - > fp & & stream - > file_handle = = INVALID_HANDLE_VALUE ) )
2020-06-25 14:01:19 +02:00
return - 1 ;
2019-01-06 14:49:26 +01:00
2020-06-25 14:01:19 +02:00
switch ( seek_position )
{
case RETRO_VFS_SEEK_POSITION_START :
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
2021-01-07 00:13:02 +08:00
SetFilePointerEx ( stream - > file_handle , _offset , NULL , FILE_BEGIN ) ;
2021-01-16 17:30:36 +01:00
else
2021-01-07 00:13:02 +08:00
stream - > fp - > Seek ( offset ) ;
2020-06-25 14:01:19 +02:00
break ;
2019-01-06 14:49:26 +01:00
2020-06-25 14:01:19 +02:00
case RETRO_VFS_SEEK_POSITION_CURRENT :
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
2021-01-07 00:13:02 +08:00
SetFilePointerEx ( stream - > file_handle , _offset , NULL , FILE_CURRENT ) ;
2021-01-16 17:30:36 +01:00
else
2021-01-07 00:13:02 +08:00
stream - > fp - > Seek ( retro_vfs_file_tell_impl ( stream ) + offset ) ;
2020-06-25 14:01:19 +02:00
break ;
2019-01-06 14:49:26 +01:00
2020-06-25 14:01:19 +02:00
case RETRO_VFS_SEEK_POSITION_END :
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
2021-01-07 00:13:02 +08:00
SetFilePointerEx ( stream - > file_handle , _offset , NULL , FILE_END ) ;
2021-01-16 17:30:36 +01:00
else
2021-01-07 00:13:02 +08:00
stream - > fp - > Seek ( stream - > fp - > Size - offset ) ;
2020-06-25 14:01:19 +02:00
break ;
}
2019-01-06 14:49:26 +01:00
2021-01-16 17:30:36 +01:00
/* For simplicity always flush the buffer on seek */
2020-06-25 14:01:19 +02:00
stream - > buffer_left = 0 ;
2019-04-27 23:47:52 +02:00
2020-06-25 14:01:19 +02:00
return 0 ;
2019-01-06 14:49:26 +01:00
}
2020-06-25 14:01:19 +02:00
int64_t retro_vfs_file_read_impl (
libretro_vfs_implementation_file * stream , void * s , uint64_t len )
2019-01-06 14:49:26 +01:00
{
2020-06-25 14:01:19 +02:00
int64_t ret ;
2021-01-07 00:13:02 +08:00
int64_t bytes_read = 0 ;
2020-06-25 14:01:19 +02:00
IBuffer ^ buffer ;
2019-04-27 23:47:52 +02:00
2021-01-07 00:13:02 +08:00
if ( ! stream | | ( ! stream - > fp & & stream - > file_handle = = INVALID_HANDLE_VALUE ) | | ! s )
return - 1 ;
2019-01-06 14:49:26 +01:00
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
{
2021-01-07 00:13:02 +08:00
DWORD _bytes_read ;
ReadFile ( stream - > file_handle , ( char * ) s , len , & _bytes_read , NULL ) ;
return ( int64_t ) _bytes_read ;
}
2021-01-16 17:30:36 +01:00
if ( len < = stream - > buffer_size )
2020-06-25 14:01:19 +02:00
{
2021-01-16 17:30:36 +01:00
/* Small read, use manually buffered I/O */
if ( stream - > buffer_left < len )
2020-06-25 14:01:19 +02:00
{
2021-01-16 17:30:36 +01:00
/* Exhaust the buffer */
memcpy ( s ,
2020-06-25 14:01:19 +02:00
& stream - > buffer [ stream - > buffer_fill - stream - > buffer_left ] ,
stream - > buffer_left ) ;
2021-01-16 17:30:36 +01:00
len - = stream - > buffer_left ;
bytes_read + = stream - > buffer_left ;
stream - > buffer_left = 0 ;
2020-06-25 14:01:19 +02:00
2021-01-16 17:30:36 +01:00
/* Fill buffer */
stream - > buffer_left = RunAsyncAndCatchErrors < int64_t > ( [ & ] ( ) {
2020-06-25 14:01:19 +02:00
return concurrency : : create_task ( stream - > fp - > ReadAsync ( stream - > bufferp , stream - > bufferp - > Capacity , InputStreamOptions : : None ) ) . then ( [ & ] ( IBuffer ^ buf ) {
2021-01-16 17:30:36 +01:00
retro_assert ( stream - > bufferp = = buf ) ;
return ( int64_t ) stream - > bufferp - > Length ;
} ) ;
2020-06-25 14:01:19 +02:00
} , - 1 ) ;
2021-01-16 17:30:36 +01:00
stream - > buffer_fill = stream - > buffer_left ;
if ( stream - > buffer_left = = - 1 )
{
stream - > buffer_left = 0 ;
stream - > buffer_fill = 0 ;
return - 1 ;
}
if ( stream - > buffer_left < len )
{
/* EOF */
memcpy ( & ( ( char * ) s ) [ bytes_read ] ,
2020-06-25 14:01:19 +02:00
stream - > buffer , stream - > buffer_left ) ;
2021-01-16 17:30:36 +01:00
bytes_read + = stream - > buffer_left ;
stream - > buffer_left = 0 ;
2020-06-25 14:01:19 +02:00
return bytes_read ;
}
2021-01-16 17:30:36 +01:00
memcpy ( & ( ( char * ) s ) [ bytes_read ] , stream - > buffer , len ) ;
bytes_read + = len ;
2020-06-25 14:01:19 +02:00
stream - > buffer_left - = len ;
2021-01-16 17:30:36 +01:00
return bytes_read ;
2020-06-25 14:01:19 +02:00
}
2021-01-16 17:30:36 +01:00
/* Internal buffer already contains requested amount */
memcpy ( s ,
& stream - > buffer [ stream - > buffer_fill - stream - > buffer_left ] ,
len ) ;
stream - > buffer_left - = len ;
return len ;
}
/* Big read exceeding buffer size,
* exhaust small buffer and read rest in one go */
memcpy ( s , & stream - > buffer [ stream - > buffer_fill - stream - > buffer_left ] , stream - > buffer_left ) ;
len - = stream - > buffer_left ;
bytes_read + = stream - > buffer_left ;
stream - > buffer_left = 0 ;
2021-01-07 00:13:02 +08:00
2021-01-16 17:30:36 +01:00
buffer = CreateNativeBuffer ( & ( ( char * ) s ) [ bytes_read ] , len , 0 ) ;
ret = RunAsyncAndCatchErrors < int64_t > ( [ & ] ( ) {
2021-01-07 00:13:02 +08:00
return concurrency : : create_task ( stream - > fp - > ReadAsync ( buffer , buffer - > Capacity - bytes_read , InputStreamOptions : : None ) ) . then ( [ & ] ( IBuffer ^ buf ) {
2021-01-16 17:30:36 +01:00
retro_assert ( buf = = buffer ) ;
return ( int64_t ) buffer - > Length ;
} ) ;
2021-01-07 00:13:02 +08:00
} , - 1 ) ;
2021-01-16 17:30:36 +01:00
if ( ret = = - 1 )
return - 1 ;
return bytes_read + ret ;
2019-01-06 14:49:26 +01:00
}
2020-06-25 14:01:19 +02:00
int64_t retro_vfs_file_write_impl (
libretro_vfs_implementation_file * stream , const void * s , uint64_t len )
2019-01-06 14:49:26 +01:00
{
2020-06-25 14:01:19 +02:00
IBuffer ^ buffer ;
2021-01-07 00:13:02 +08:00
if ( ! stream | | ( ! stream - > fp & & stream - > file_handle = = INVALID_HANDLE_VALUE ) | | ! s )
2020-06-25 14:01:19 +02:00
return - 1 ;
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
{
2021-01-07 00:13:02 +08:00
DWORD bytes_written ;
WriteFile ( stream - > file_handle , s , len , & bytes_written , NULL ) ;
return ( int64_t ) bytes_written ;
2021-01-16 17:30:36 +01:00
}
/* const_cast to remove const modifier is undefined behaviour, but the buffer is only read, should be safe */
buffer = CreateNativeBuffer ( const_cast < void * > ( s ) , len , len ) ;
return RunAsyncAndCatchErrors < int64_t > ( [ & ] ( ) {
2020-06-25 14:01:19 +02:00
return concurrency : : create_task ( stream - > fp - > WriteAsync ( buffer ) ) . then ( [ & ] ( unsigned int written ) {
2021-01-16 17:30:36 +01:00
return ( int64_t ) written ;
} ) ;
2020-06-25 14:01:19 +02:00
} , - 1 ) ;
2019-01-06 14:49:26 +01:00
}
int retro_vfs_file_flush_impl ( libretro_vfs_implementation_file * stream )
{
2021-01-07 00:13:02 +08:00
if ( ! stream | | ( ! stream - > fp & & stream - > file_handle = = INVALID_HANDLE_VALUE ) | | ! stream - > fp )
return - 1 ;
2021-01-16 17:30:36 +01:00
if ( stream - > file_handle ! = INVALID_HANDLE_VALUE )
{
2021-01-07 00:13:02 +08:00
FlushFileBuffers ( stream - > file_handle ) ;
return 0 ;
2021-01-16 17:30:36 +01:00
}
return RunAsyncAndCatchErrors < int > ( [ & ] ( ) {
2021-01-07 00:13:02 +08:00
return concurrency : : create_task ( stream - > fp - > FlushAsync ( ) ) . then ( [ & ] ( bool this_value_is_not_even_documented_wtf ) {
2021-01-16 17:30:36 +01:00
/* The bool value may be reporting an error or something, but just leave it alone for now */
/* https://github.com/MicrosoftDocs/winrt-api/issues/841 */
return 0 ;
} ) ;
2021-01-07 00:13:02 +08:00
} , - 1 ) ;
2019-01-06 14:49:26 +01:00
}
int retro_vfs_file_remove_impl ( const char * path )
{
2021-01-16 17:30:36 +01:00
BOOL result ;
2020-06-25 14:01:19 +02:00
wchar_t * path_wide ;
2021-01-07 00:13:02 +08:00
if ( ! path | | ! * path )
return - 1 ;
path_wide = utf8_to_utf16_string_alloc ( path ) ;
windowsize_path ( path_wide ) ;
2020-06-25 14:01:19 +02:00
2021-01-07 00:13:02 +08:00
/* Try Win32 first, this should work in AppData */
2021-08-25 15:54:09 +01:00
result = DeleteFileFromAppW ( path_wide ) ;
2021-08-26 23:18:42 +01:00
free ( path_wide ) ;
2021-01-16 17:30:36 +01:00
if ( result )
2021-01-07 00:13:02 +08:00
return 0 ;
2021-08-25 15:54:09 +01:00
return - 1 ;
2019-01-06 14:49:26 +01:00
}
/* TODO: this may not work if trying to move a directory */
2021-08-25 15:54:09 +01:00
/*int retro_vfs_file_rename_impl(const char* old_path, const char* new_path)
2019-01-06 14:49:26 +01:00
{
2020-08-18 14:47:57 +02:00
char new_file_name [ PATH_MAX_LENGTH ] ;
char new_dir_path [ PATH_MAX_LENGTH ] ;
2020-06-25 14:01:19 +02:00
wchar_t * new_file_name_wide ;
wchar_t * old_path_wide , * new_dir_path_wide ;
Platform : : String ^ old_path_str ;
Platform : : String ^ new_dir_path_str ;
Platform : : String ^ new_file_name_str ;
if ( ! old_path | | ! * old_path | | ! new_path | | ! * new_path )
2021-01-07 00:13:02 +08:00
return - 1 ;
2019-01-06 14:49:26 +01:00
2020-08-18 14:47:57 +02:00
new_file_name [ 0 ] = ' \0 ' ;
new_dir_path [ 0 ] = ' \0 ' ;
2021-01-07 00:13:02 +08:00
old_path_wide = utf8_to_utf16_string_alloc ( old_path ) ;
old_path_str = ref new Platform : : String ( old_path_wide ) ;
free ( old_path_wide ) ;
fill_pathname_basedir ( new_dir_path , new_path , sizeof ( new_dir_path ) ) ;
new_dir_path_wide = utf8_to_utf16_string_alloc ( new_dir_path ) ;
windowsize_path ( new_dir_path_wide ) ;
new_dir_path_str = ref new Platform : : String ( new_dir_path_wide ) ;
free ( new_dir_path_wide ) ;
fill_pathname_base ( new_file_name , new_path , sizeof ( new_file_name ) ) ;
new_file_name_wide = utf8_to_utf16_string_alloc ( new_file_name ) ;
new_file_name_str = ref new Platform : : String ( new_file_name_wide ) ;
free ( new_file_name_wide ) ;
retro_assert ( ! old_path_str - > IsEmpty ( ) & & ! new_dir_path_str - > IsEmpty ( ) & & ! new_file_name_str - > IsEmpty ( ) ) ;
return RunAsyncAndCatchErrors < int > ( [ & ] ( ) {
concurrency : : task < StorageFile ^ > old_file_task = concurrency : : create_task ( LocateStorageItem < StorageFile > ( old_path_str ) ) ;
concurrency : : task < StorageFolder ^ > new_dir_task = concurrency : : create_task ( LocateStorageItem < StorageFolder > ( new_dir_path_str ) ) ;
return concurrency : : create_task ( [ & ] {
2021-08-25 15:54:09 +01:00
// Run these two tasks in parallel
// TODO: There may be some cleaner way to express this
2021-01-07 00:13:02 +08:00
concurrency : : task_group group ;
group . run ( [ & ] { return old_file_task ; } ) ;
group . run ( [ & ] { return new_dir_task ; } ) ;
group . wait ( ) ;
} ) . then ( [ & ] ( ) {
return old_file_task . get ( ) - > MoveAsync ( new_dir_task . get ( ) , new_file_name_str , NameCollisionOption : : ReplaceExisting ) ;
} ) . then ( [ & ] ( ) {
return 0 ;
} ) ;
} , - 1 ) ;
2021-08-25 15:54:09 +01:00
} */
//this is enables you to copy access permissions from one file/folder to another
//however depending on the target and where the file is being transferred to and from it may not be needed.
//(use disgression)
int uwp_copy_acl ( const wchar_t * source , const wchar_t * target )
{
PSECURITY_DESCRIPTOR sidOwnerDescriptor = nullptr ;
PSECURITY_DESCRIPTOR sidGroupDescriptor = nullptr ;
PSECURITY_DESCRIPTOR daclDescriptor = nullptr ;
PSID sidOwner ;
PSID sidGroup ;
PACL dacl ;
PACL sacl ;
DWORD result ;
HANDLE original_file = CreateFileFromAppW ( source , GENERIC_READ , 0 , nullptr , OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , nullptr ) ;
if ( original_file ! = INVALID_HANDLE_VALUE )
{
result = GetSecurityInfo ( original_file , SE_FILE_OBJECT , DACL_SECURITY_INFORMATION , & sidOwner , & sidGroup , & dacl , & sacl , & daclDescriptor ) ;
if ( result ! = 0 )
{
LocalFree ( daclDescriptor ) ;
CloseHandle ( original_file ) ;
return result ;
}
result = GetSecurityInfo ( original_file , SE_FILE_OBJECT , OWNER_SECURITY_INFORMATION , & sidOwner , & sidGroup , & dacl , & sacl , & sidOwnerDescriptor ) ;
if ( result ! = 0 )
{
LocalFree ( sidOwnerDescriptor ) ;
LocalFree ( daclDescriptor ) ;
CloseHandle ( original_file ) ;
return result ;
}
result = GetSecurityInfo ( original_file , SE_FILE_OBJECT , GROUP_SECURITY_INFORMATION , & sidOwner , & sidGroup , & dacl , & sacl , & sidGroupDescriptor ) ;
//close file handle regardless of result
CloseHandle ( original_file ) ;
if ( result ! = 0 )
{
LocalFree ( sidOwnerDescriptor ) ;
LocalFree ( sidGroupDescriptor ) ;
LocalFree ( daclDescriptor ) ;
CloseHandle ( original_file ) ;
return result ;
}
CloseHandle ( original_file ) ;
}
else
{
result = GetNamedSecurityInfoW ( source , SE_FILE_OBJECT , DACL_SECURITY_INFORMATION , & sidOwner , & sidGroup , & dacl , & sacl , & daclDescriptor ) ;
if ( result ! = 0 )
{
LocalFree ( daclDescriptor ) ;
return result ;
}
result = GetNamedSecurityInfoW ( source , SE_FILE_OBJECT , OWNER_SECURITY_INFORMATION , & sidOwner , & sidGroup , & dacl , & sacl , & sidOwnerDescriptor ) ;
if ( result ! = 0 )
{
LocalFree ( sidOwnerDescriptor ) ;
LocalFree ( daclDescriptor ) ;
return result ;
}
result = GetNamedSecurityInfoW ( source , SE_FILE_OBJECT , GROUP_SECURITY_INFORMATION , & sidOwner , & sidGroup , & dacl , & sacl , & sidGroupDescriptor ) ;
if ( result ! = 0 )
{
LocalFree ( sidOwnerDescriptor ) ;
LocalFree ( sidGroupDescriptor ) ;
LocalFree ( daclDescriptor ) ;
return result ;
}
}
SECURITY_INFORMATION info = DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION ;
HANDLE target_file = CreateFileFromAppW ( target , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , nullptr , OPEN_EXISTING , 0 , nullptr ) ;
if ( target_file ! = INVALID_HANDLE_VALUE )
{
result = SetSecurityInfo ( target_file , SE_FILE_OBJECT , info , sidOwner , sidGroup , dacl , sacl ) ;
CloseHandle ( target_file ) ;
}
else
{
wchar_t * temp = wcsdup ( target ) ;
result = SetNamedSecurityInfoW ( temp , SE_FILE_OBJECT , info , sidOwner , sidGroup , dacl , sacl ) ;
free ( temp ) ;
}
if ( result ! = 0 )
{
LocalFree ( sidOwnerDescriptor ) ;
LocalFree ( sidGroupDescriptor ) ;
LocalFree ( daclDescriptor ) ;
return result ;
}
if ( ( sidOwnerDescriptor ! = nullptr & & LocalFree ( sidOwnerDescriptor ) ! = nullptr ) | | ( daclDescriptor ! = nullptr & & LocalFree ( daclDescriptor ) ! = nullptr ) | | ( daclDescriptor ! = nullptr & & LocalFree ( daclDescriptor ) ! = nullptr ) )
{
//an error occured but idk what error code is right so we just return -1
return - 1 ;
}
//woo we made it all the way to the end so we can return success
return 0 ;
}
int uwp_mkdir_impl ( std : : experimental : : filesystem : : path dir )
{
//I feel like this should create the directory recursively but the existing implementation does not so this update won't
2021-08-26 23:18:42 +01:00
//I put in the work but I just commented out the stuff you would need
2021-08-25 15:54:09 +01:00
WIN32_FILE_ATTRIBUTE_DATA lpFileInfo ;
bool parent_dir_exists = false ;
if ( dir . empty ( ) )
return - 1 ;
//check if file attributes can be gotten successfully
if ( GetFileAttributesExFromAppW ( dir . parent_path ( ) . wstring ( ) . c_str ( ) , GetFileExInfoStandard , & lpFileInfo ) )
{
//check that the files attributes are not null or empty
if ( lpFileInfo . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES & & lpFileInfo . dwFileAttributes ! = 0 )
{
if ( lpFileInfo . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
parent_dir_exists = true ;
}
}
}
if ( ! parent_dir_exists )
{
//try to create parent dir
int success = uwp_mkdir_impl ( dir . parent_path ( ) ) ;
2021-08-26 23:18:42 +01:00
if ( success ! = 0 & & success ! = - 2 )
2021-08-25 15:54:09 +01:00
return success ;
}
/* Try Win32 first, this should work in AppData */
bool create_dir = CreateDirectoryFromAppW ( dir . wstring ( ) . c_str ( ) , NULL ) ;
if ( create_dir )
return 0 ;
if ( GetLastError ( ) = = ERROR_ALREADY_EXISTS )
return - 2 ;
return - 1 ;
}
int retro_vfs_mkdir_impl ( const char * dir )
{
return uwp_mkdir_impl ( std : : filesystem : : path ( dir ) ) ;
}
//the first run paramater is used to avoid error checking when doing recursion
//unlike the initial implementation this can move folders even empty ones when you want to move a directory structure
//this will fail even if a single file cannot be moved
int uwp_move_path ( std : : filesystem : : path old_path , std : : filesystem : : path new_path , bool firstrun = true )
{
if ( old_path . empty ( ) | | new_path . empty ( ) )
return - 1 ;
if ( firstrun )
{
WIN32_FILE_ATTRIBUTE_DATA lpFileInfo , targetfileinfo ;
bool parent_dir_exists = false ;
//make sure that parent path exists
if ( GetFileAttributesExFromAppW ( new_path . parent_path ( ) . wstring ( ) . c_str ( ) , GetFileExInfoStandard , & lpFileInfo ) )
{
//check that the files attributes are not null or empty
if ( lpFileInfo . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES & & lpFileInfo . dwFileAttributes ! = 0 )
{
if ( ! ( lpFileInfo . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
{
//parent path doesn't exist ;-; so we gotta create it
uwp_mkdir_impl ( new_path . parent_path ( ) ) ;
}
}
}
//make sure that source path exists
if ( GetFileAttributesExFromAppW ( old_path . wstring ( ) . c_str ( ) , GetFileExInfoStandard , & lpFileInfo ) )
{
//check that the files attributes are not null or empty
if ( lpFileInfo . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES & & lpFileInfo . dwFileAttributes ! = 0 )
{
//check if source path is a dir
if ( lpFileInfo . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
//create the target dir
CreateDirectoryFromAppW ( new_path . wstring ( ) . c_str ( ) , NULL ) ;
//call move function again but with first run disabled in order to move the folder
int result = uwp_move_path ( old_path , new_path , false ) ;
if ( result ! = 0 )
{
//return the error
return result ;
}
}
else
{
//the file that we want to move exists so we can copy it now
//check if target file already exists
if ( GetFileAttributesExFromAppW ( new_path . wstring ( ) . c_str ( ) , GetFileExInfoStandard , & targetfileinfo ) )
{
if ( targetfileinfo . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES & & targetfileinfo . dwFileAttributes ! = 0 & & ( ! ( targetfileinfo . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) )
{
//delete target file
if ( DeleteFileFromAppW ( new_path . wstring ( ) . c_str ( ) ) )
{
//return an error if we can't successfully delete the target file
return - 1 ;
}
}
}
//move the file
if ( ! MoveFileFromAppW ( old_path . wstring ( ) . c_str ( ) , new_path . wstring ( ) . c_str ( ) ) )
{
//failed to move the file
return - 1 ;
}
//set acl - this step fucking sucks or at least to before I made a whole ass function
//idk if we actually "need" to set the acl though
if ( uwp_copy_acl ( new_path . parent_path ( ) . wstring ( ) . c_str ( ) , new_path . wstring ( ) . c_str ( ) ) ! = 0 )
{
//setting acl failed
return - 1 ;
}
}
}
}
}
else
{
//we are bypassing error checking and moving a dir
//first we gotta get a list of files in the dir
wchar_t * filteredPath = wcsdup ( old_path . wstring ( ) . c_str ( ) ) ;
wcscat_s ( filteredPath , sizeof ( L " \\ *.* " ) , L " \\ *.* " ) ;
WIN32_FIND_DATA findDataResult ;
HANDLE searchResults = FindFirstFileExFromAppW ( filteredPath , FindExInfoBasic , & findDataResult , FindExSearchNameMatch , nullptr , FIND_FIRST_EX_LARGE_FETCH ) ;
if ( searchResults ! = INVALID_HANDLE_VALUE )
{
bool fail = false ;
do
{
2021-09-06 13:27:49 +01:00
if ( wcscmp ( findDataResult . cFileName , L " . " ) ! = 0 & & wcscmp ( findDataResult . cFileName , L " .. " ) ! = 0 )
2021-08-25 15:54:09 +01:00
{
std : : filesystem : : path temp_old = old_path ;
std : : filesystem : : path temp_new = new_path ;
temp_old / = findDataResult . cFileName ;
temp_new / = findDataResult . cFileName ;
if ( findDataResult . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
CreateDirectoryFromAppW ( temp_new . wstring ( ) . c_str ( ) , NULL ) ;
int result = uwp_move_path ( temp_old , temp_new , false ) ;
if ( result ! = 0 )
fail = true ;
}
else
{
WIN32_FILE_ATTRIBUTE_DATA targetfileinfo ;
//the file that we want to move exists so we can copy it now
//check if target file already exists
if ( GetFileAttributesExFromAppW ( temp_new . wstring ( ) . c_str ( ) , GetFileExInfoStandard , & targetfileinfo ) )
{
if ( targetfileinfo . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES & & targetfileinfo . dwFileAttributes ! = 0 & & ( ! ( targetfileinfo . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) )
{
//delete target file
if ( DeleteFileFromAppW ( temp_new . wstring ( ) . c_str ( ) ) )
{
//return an error if we can't successfully delete the target file
fail = true ;
}
}
}
//move the file
if ( ! MoveFileFromAppW ( temp_old . wstring ( ) . c_str ( ) , temp_new . wstring ( ) . c_str ( ) ) )
{
//failed to move the file
fail = true ;
}
//set acl - this step fucking sucks or at least to before I made a whole ass function
//idk if we actually "need" to set the acl though
if ( uwp_copy_acl ( new_path . wstring ( ) . c_str ( ) , temp_new . wstring ( ) . c_str ( ) ) ! = 0 )
{
//setting acl failed
fail = true ;
}
}
}
} while ( FindNextFile ( searchResults , & findDataResult ) ) ;
FindClose ( searchResults ) ;
if ( fail )
return - 1 ;
}
free ( filteredPath ) ;
}
//yooooooo we finally made it all the way to the end
//we can now return success
return 0 ;
}
//c doesn't support default arguments so we wrap it up in a shell to enable us to use default arguments
//default arguments mean that we can do better recursion
int retro_vfs_file_rename_impl ( const char * old_path , const char * new_path )
{
return uwp_move_path ( std : : filesystem : : path ( old_path ) , std : : filesystem : : path ( old_path ) ) ;
2019-01-06 14:49:26 +01:00
}
const char * retro_vfs_file_get_path_impl ( libretro_vfs_implementation_file * stream )
{
2021-01-07 00:13:02 +08:00
/* should never happen, do something noisy so caller can be fixed */
if ( ! stream )
abort ( ) ;
return stream - > orig_path ;
2019-01-06 14:49:26 +01:00
}
int retro_vfs_stat_impl ( const char * path , int32_t * size )
{
2020-06-25 14:01:19 +02:00
wchar_t * path_wide ;
2021-07-31 04:36:08 +01:00
_WIN32_FILE_ATTRIBUTE_DATA attribdata ;
2020-06-25 14:01:19 +02:00
2021-01-07 00:13:02 +08:00
if ( ! path | | ! * path )
return 0 ;
path_wide = utf8_to_utf16_string_alloc ( path ) ;
windowsize_path ( path_wide ) ;
/* Try Win32 first, this should work in AppData */
2021-08-25 15:54:09 +01:00
if ( GetFileAttributesExFromAppW ( path_wide , GetFileExInfoStandard , & attribdata ) )
2021-01-16 17:30:36 +01:00
{
2021-08-25 15:54:09 +01:00
if ( attribdata . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES )
2021-07-31 04:36:08 +01:00
{
2021-08-25 15:54:09 +01:00
if ( ! ( attribdata . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
2021-07-31 04:36:08 +01:00
{
LARGE_INTEGER sz ;
if ( size )
{
sz . HighPart = attribdata . nFileSizeHigh ;
sz . LowPart = attribdata . nFileSizeLow ;
2021-08-13 20:33:27 +01:00
* size = sz . QuadPart ;
2021-07-31 04:36:08 +01:00
}
}
2021-08-26 23:18:42 +01:00
free ( path_wide ) ;
2021-08-25 15:54:09 +01:00
return ( attribdata . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ? RETRO_VFS_STAT_IS_VALID | RETRO_VFS_STAT_IS_DIRECTORY : RETRO_VFS_STAT_IS_VALID ;
2021-07-31 04:36:08 +01:00
}
2021-01-16 17:30:36 +01:00
}
2021-08-26 23:18:42 +01:00
free ( path_wide ) ;
2021-08-25 15:54:09 +01:00
return 0 ;
2019-01-06 14:49:26 +01:00
}
# ifdef VFS_FRONTEND
struct retro_vfs_dir_handle
# else
struct libretro_vfs_implementation_dir
# endif
{
2021-01-07 00:13:02 +08:00
IVectorView < IStorageItem ^ > ^ directory ;
IIterator < IStorageItem ^ > ^ entry ;
char * entry_name ;
2019-01-06 14:49:26 +01:00
} ;
2021-09-06 13:27:49 +01:00
libretro_vfs_implementation_dir * retro_vfs_opendir_impl ( const char * name , bool include_hidden )
2019-01-06 14:49:26 +01:00
{
2021-09-06 13:27:49 +01:00
wchar_t * name_wide ;
Platform : : String ^ name_str ;
libretro_vfs_implementation_dir * rdir ;
2019-01-06 14:49:26 +01:00
2021-09-06 13:27:49 +01:00
if ( ! name | | ! * name )
return NULL ;
2019-01-06 14:49:26 +01:00
2021-09-06 13:27:49 +01:00
rdir = ( libretro_vfs_implementation_dir * ) calloc ( 1 , sizeof ( * rdir ) ) ;
if ( ! rdir )
return NULL ;
2019-01-06 14:49:26 +01:00
2021-09-06 13:27:49 +01:00
name_wide = utf8_to_utf16_string_alloc ( name ) ;
windowsize_path ( name_wide ) ;
name_str = ref new Platform : : String ( name_wide ) ;
free ( name_wide ) ;
2019-01-06 14:49:26 +01:00
2021-09-06 13:27:49 +01:00
WIN32_FILE_ATTRIBUTE_DATA lpFileInfo ;
std : : filesystem : : path dir ( name ) ;
if ( dir . empty ( ) )
return NULL ;
if ( ! ( rdir - > directory ) )
{
//check if file attributes can be gotten successfully
if ( GetFileAttributesExFromAppW ( dir . parent_path ( ) . wstring ( ) . c_str ( ) , GetFileExInfoStandard , & lpFileInfo ) )
{
//check that the files attributes are not null or empty
if ( lpFileInfo . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES & & lpFileInfo . dwFileAttributes ! = 0 )
{
if ( lpFileInfo . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
std : : wstring filteredPath ( dir . wstring ( ) . c_str ( ) ) ;
WIN32_FIND_DATA findDataResult ;
if ( filteredPath [ filteredPath . size ( ) - 1 ] = = ' \\ ' )
filteredPath . erase ( filteredPath . size ( ) - 1 ) ;
filteredPath + = L " \\ *.* " ;
HANDLE searchResults = FindFirstFileExFromAppW ( filteredPath . c_str ( ) , FindExInfoBasic , & findDataResult , FindExSearchNameMatch , nullptr , FIND_FIRST_EX_LARGE_FETCH ) ;
if ( searchResults ! = INVALID_HANDLE_VALUE )
{
Platform : : Collections : : Vector < IStorageItem ^ > ^ result = ref new Platform : : Collections : : Vector < IStorageItem ^ > ( ) ;
do
{
if ( wcscmp ( findDataResult . cFileName , L " . " ) ! = 0 & & wcscmp ( findDataResult . cFileName , L " .. " ) ! = 0 )
{
if ( ! ( ( findDataResult . dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) | | ( findDataResult . dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) ) )
{
std : : filesystem : : path temp_new = dir ;
temp_new / = findDataResult . cFileName ;
std : : wstring temp_path = temp_new . generic_wstring ( ) ;
while ( true ) {
size_t p = temp_path . find ( L " / " ) ;
if ( p = = std : : wstring : : npos ) break ;
temp_path . replace ( p , 1 , L " \\ " ) ;
}
IStorageItem ^ item ;
if ( findDataResult . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
item = RunAsyncAndCatchErrors < StorageFolder ^ > ( [ & ] ( ) {
return concurrency : : create_task ( LocateStorageItem < StorageFolder > ( ref new Platform : : String ( temp_path . c_str ( ) ) ) ) ;
} , nullptr ) ;
}
else
{
item = RunAsyncAndCatchErrors < StorageFile ^ > ( [ & ] ( ) {
return concurrency : : create_task ( LocateStorageItem < StorageFile > ( ref new Platform : : String ( temp_path . c_str ( ) ) ) ) ;
} , nullptr ) ;
}
if ( item )
if ( result )
result - > Append ( item ) ;
}
}
} while ( FindNextFile ( searchResults , & findDataResult ) ) ;
FindClose ( searchResults ) ;
if ( result )
rdir - > directory = result - > GetView ( ) ;
}
}
}
}
}
2021-01-07 00:13:02 +08:00
if ( rdir - > directory )
return rdir ;
2019-01-06 14:49:26 +01:00
2021-01-07 00:13:02 +08:00
free ( rdir ) ;
return NULL ;
2019-01-06 14:49:26 +01:00
}
bool retro_vfs_readdir_impl ( libretro_vfs_implementation_dir * rdir )
{
2021-01-07 00:13:02 +08:00
if ( ! rdir - > entry )
{
rdir - > entry = rdir - > directory - > First ( ) ;
return rdir - > entry - > HasCurrent ;
}
2020-06-25 14:01:19 +02:00
return rdir - > entry - > MoveNext ( ) ;
2019-01-06 14:49:26 +01:00
}
2020-06-25 14:01:19 +02:00
const char * retro_vfs_dirent_get_name_impl (
libretro_vfs_implementation_dir * rdir )
2019-01-06 14:49:26 +01:00
{
2021-01-07 00:13:02 +08:00
if ( rdir - > entry_name )
free ( rdir - > entry_name ) ;
rdir - > entry_name = utf16_to_utf8_string_alloc (
2020-06-25 14:01:19 +02:00
rdir - > entry - > Current - > Name - > Data ( ) ) ;
2021-01-07 00:13:02 +08:00
return rdir - > entry_name ;
2019-01-06 14:49:26 +01:00
}
bool retro_vfs_dirent_is_dir_impl ( libretro_vfs_implementation_dir * rdir )
{
2021-01-07 00:13:02 +08:00
return rdir - > entry - > Current - > IsOfType ( StorageItemTypes : : Folder ) ;
2019-01-06 14:49:26 +01:00
}
int retro_vfs_closedir_impl ( libretro_vfs_implementation_dir * rdir )
{
2021-01-07 00:13:02 +08:00
if ( ! rdir )
return - 1 ;
2019-01-06 14:49:26 +01:00
2021-01-07 00:13:02 +08:00
if ( rdir - > entry_name )
free ( rdir - > entry_name ) ;
rdir - > entry = nullptr ;
rdir - > directory = nullptr ;
2019-01-06 14:49:26 +01:00
2021-01-07 00:13:02 +08:00
free ( rdir ) ;
return 0 ;
2019-01-06 14:49:26 +01:00
}
2019-01-14 21:30:20 +01:00
2019-01-14 22:43:29 +01:00
char * uwp_trigger_picker ( void )
{
2021-01-07 00:13:02 +08:00
return RunAsyncAndCatchErrors < char * > ( [ & ] ( ) {
return TriggerPickerAddDialog ( ) . then ( [ ] ( Platform : : String ^ path ) {
return utf16_to_utf8_string_alloc ( path - > Data ( ) ) ;
} ) ;
} , NULL ) ;
2019-01-14 22:43:29 +01:00
}