mirror of
https://github.com/libretro/libretro-common.git
synced 2024-11-23 16:19:50 +00:00
Updates
This commit is contained in:
parent
195ab74542
commit
2aefcd8e94
@ -1722,6 +1722,37 @@ enum retro_mod
|
||||
* Must be called in retro_set_environment().
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_SET_VARIABLE 70
|
||||
/* const struct retro_variable * --
|
||||
* Allows an implementation to notify the frontend
|
||||
* that a core option value has changed.
|
||||
*
|
||||
* retro_variable::key and retro_variable::value
|
||||
* must match strings that have been set previously
|
||||
* via one of the following:
|
||||
*
|
||||
* - RETRO_ENVIRONMENT_SET_VARIABLES
|
||||
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS
|
||||
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL
|
||||
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2
|
||||
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL
|
||||
*
|
||||
* After changing a core option value via this
|
||||
* callback, RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE
|
||||
* will return true.
|
||||
*
|
||||
* If data is NULL, no changes will be registered
|
||||
* and the callback will return true; an
|
||||
* implementation may therefore pass NULL in order
|
||||
* to test whether the callback is supported.
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_GET_THROTTLE_STATE (71 | RETRO_ENVIRONMENT_EXPERIMENTAL)
|
||||
/* struct retro_throttle_state * --
|
||||
* Allows an implementation to get details on the actual rate
|
||||
* the frontend is attempting to call retro_run().
|
||||
*/
|
||||
|
||||
/* VFS functionality */
|
||||
|
||||
/* File paths:
|
||||
@ -3667,6 +3698,43 @@ struct retro_fastforwarding_override
|
||||
bool inhibit_toggle;
|
||||
};
|
||||
|
||||
/* During normal operation. Rate will be equal to the core's internal FPS. */
|
||||
#define RETRO_THROTTLE_NONE 0
|
||||
|
||||
/* While paused or stepping single frames. Rate will be 0. */
|
||||
#define RETRO_THROTTLE_FRAME_STEPPING 1
|
||||
|
||||
/* During fast forwarding.
|
||||
* Rate will be 0 if not specifically limited to a maximum speed. */
|
||||
#define RETRO_THROTTLE_FAST_FORWARD 2
|
||||
|
||||
/* During slow motion. Rate will be less than the core's internal FPS. */
|
||||
#define RETRO_THROTTLE_SLOW_MOTION 3
|
||||
|
||||
/* While rewinding recorded save states. Rate can vary depending on the rewind
|
||||
* speed or be 0 if the frontend is not aiming for a specific rate. */
|
||||
#define RETRO_THROTTLE_REWINDING 4
|
||||
|
||||
/* While vsync is active in the video driver and the target refresh rate is
|
||||
* lower than the core's internal FPS. Rate is the target refresh rate. */
|
||||
#define RETRO_THROTTLE_VSYNC 5
|
||||
|
||||
/* When the frontend does not throttle in any way. Rate will be 0.
|
||||
* An example could be if no vsync or audio output is active. */
|
||||
#define RETRO_THROTTLE_UNBLOCKED 6
|
||||
|
||||
struct retro_throttle_state
|
||||
{
|
||||
/* The current throttling mode. Should be one of the values above. */
|
||||
unsigned mode;
|
||||
|
||||
/* How many times per second the frontend aims to call retro_run.
|
||||
* Depending on the mode, it can be 0 if there is no known fixed rate.
|
||||
* This won't be accurate if the total processing time of the core and
|
||||
* the frontend is longer than what is available for one frame. */
|
||||
float rate;
|
||||
};
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
/* Environment callback. Gives implementations a way of performing
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <stdio.h>
|
||||
#include <wrl.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <windows.storage.streams.h>
|
||||
#include <robuffer.h>
|
||||
#include <collection.h>
|
||||
#include <functional>
|
||||
@ -36,12 +35,6 @@
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
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
|
||||
@ -58,7 +51,6 @@ using namespace Windows::Storage::FileProperties;
|
||||
#include <string/stdstring.h>
|
||||
#include <retro_environment.h>
|
||||
#include <uwp/uwp_async.h>
|
||||
#include <uwp/uwp_file_handle_access.h>
|
||||
#include <uwp/std_filesystem_compat.h>
|
||||
|
||||
// define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING to silence warnings
|
||||
@ -84,241 +76,6 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/* 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));
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
std::wstring file_path;
|
||||
std::wstring folder_path = folder->Path->Data();
|
||||
size_t folder_path_size = folder_path.size();
|
||||
/* 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);
|
||||
|
||||
file_path = path->Data();
|
||||
|
||||
if (file_path.find(folder_path) == 0)
|
||||
{
|
||||
/* Found a match */
|
||||
file_path = file_path.length() > folder_path.length()
|
||||
? file_path.substr(folder_path.length() + 1)
|
||||
: L"";
|
||||
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)
|
||||
{
|
||||
//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.");
|
||||
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) {
|
||||
// Retry
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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 :
|
||||
public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>,
|
||||
ABI::Windows::Storage::Streams::IBuffer,
|
||||
Windows::Storage::Streams::IBufferByteAccess>
|
||||
{
|
||||
public:
|
||||
virtual ~NativeBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT __stdcall RuntimeClassInitialize(
|
||||
byte *buffer, uint32_t capacity, uint32_t length)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
byte *m_buffer;
|
||||
uint32_t m_capacity;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
IBuffer^ CreateNativeBuffer(void* buf, uint32_t capacity, uint32_t length)
|
||||
{
|
||||
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;
|
||||
if (SUCCEEDED(abiPointer.As(&handleAccess)))
|
||||
{
|
||||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (SUCCEEDED(handleAccess->Create(accessMode,
|
||||
HANDLE_SHARING_OPTIONS::HSO_SHARE_READ,
|
||||
HANDLE_OPTIONS::HO_NONE,
|
||||
nullptr,
|
||||
&hFile)))
|
||||
{
|
||||
*handle = hFile;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
|
||||
#ifdef VFS_FRONTEND
|
||||
@ -401,9 +158,16 @@ int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file* stream, i
|
||||
|
||||
int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file* stream)
|
||||
{
|
||||
if (!stream)
|
||||
if (!stream || (!stream->fp && stream->fh == INVALID_HANDLE_VALUE))
|
||||
return -1;
|
||||
|
||||
if (stream->fh != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
LARGE_INTEGER sz;
|
||||
if (GetFileSizeEx(stream->fh, &sz))
|
||||
return sz.QuadPart;
|
||||
return 0;
|
||||
}
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
return ftell(stream->fp);
|
||||
@ -456,8 +220,15 @@ int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file* stream,
|
||||
int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file* stream,
|
||||
void* s, uint64_t len)
|
||||
{
|
||||
if (!stream || !s)
|
||||
return -1;
|
||||
if (!stream || (!stream->fp && stream->fh == INVALID_HANDLE_VALUE) || !s)
|
||||
return -1;
|
||||
|
||||
if (stream->fh != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD _bytes_read;
|
||||
ReadFile(stream->fh, (char*)s, len, &_bytes_read, NULL);
|
||||
return (int64_t)_bytes_read;
|
||||
}
|
||||
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
@ -1138,12 +909,3 @@ int retro_vfs_closedir_impl(libretro_vfs_implementation_dir* rdir)
|
||||
free(rdir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* uwp_trigger_picker(void)
|
||||
{
|
||||
return RunAsyncAndCatchErrors<char*>([&]() {
|
||||
return TriggerPickerAddDialog().then([](Platform::String^ path) {
|
||||
return utf16_to_utf8_string_alloc(path->Data());
|
||||
});
|
||||
}, NULL);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user