From 72a02b764788294bba2fe430a482002486f4802a Mon Sep 17 00:00:00 2001 From: orbea Date: Sun, 11 Feb 2018 09:35:58 -0800 Subject: [PATCH 001/517] qb: Add --sysconfdir This also deprecates --global-config-dir. --- CHANGES.md | 5 ++--- qb/qb.params.sh | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4ea542a340..f0c6b9ccf6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -36,9 +36,8 @@ - LOCALIZATION: Update Spanish translation. - NETPLAY: Add menu option to select different MITM (relay) server locations. - OSX: Modify HID buttons detection algorithm. -- QB: Added --datarootdir. -- QB: Added --bindir and --mandir and deprecated --with-bin_dir and --with-man_dir. -- QB: Added --docdir. +- QB: Added --datarootdir, --sysconfdir, --bindir, --docdir and --mandir. +- QB: Deprecated --global-config-dir, --with-bin_dir and --with-man_dir. - SHADERS: Allow saving of shader presets based on the parent directory (Saving one for */foo/bar/mario.sfc* would result in *shaders/presets/corename/bar.ext*). We decided it's safer to still isolate the presets to a single core because different cores may treat video output differently. - SHADERS: Don't save the path to the current preset to the main config. This was causing weird behavior, instead it will try to load *currentconfig.ext* and it will save a preset with that name when select *apply shader preset*. The resulting shader will restore properly after restarting and even after core/parent/game specific presets are loaded - SOLARIS: Initial port. diff --git a/qb/qb.params.sh b/qb/qb.params.sh index 8163c3c5c9..2a68f0605e 100644 --- a/qb/qb.params.sh +++ b/qb/qb.params.sh @@ -33,11 +33,12 @@ General environment variables: General options: EOF print_help_option "--prefix=PATH" "Install path prefix" - print_help_option "--global-config-dir=PATH" "System wide config file prefix" + print_help_option "--sysconfdir=PATH" "System wide config file prefix" print_help_option "--bindir=PATH" "Binary install directory" print_help_option "--datarootdir=PATH" "Read-only data install directory" print_help_option "--docdir=PATH" "Documentation install directory" print_help_option "--mandir=PATH" "Manpage install directory" + print_help_option "--global-config-dir=PATH" "System wide config file prefix (Deprecated)" print_help_option "--build=BUILD" "The build system (no-op)" print_help_option "--host=HOST" "Cross-compile with HOST-gcc instead of gcc" print_help_option "--help" "Show this help" @@ -87,7 +88,7 @@ parse_input() # Parse stuff :V while [ "$1" ]; do case "$1" in --prefix=*) PREFIX=${1##--prefix=};; - --global-config-dir=*) GLOBAL_CONFIG_DIR=${1##--global-config-dir=};; + --global-config-dir=*|--sysconfdir=*) GLOBAL_CONFIG_DIR="${1#*=}";; --bindir=*) BIN_DIR="${1#*=}";; --build=*) BUILD="${1#*=}";; --datarootdir=*) SHARE_DIR="${1#*=}";; From 176cde31fca575c7c7d5e6288ae2fee2041a5578 Mon Sep 17 00:00:00 2001 From: meleu Date: Thu, 15 Mar 2018 07:43:47 -0300 Subject: [PATCH 002/517] decreasing verbosity in Achievements menu --- intl/msg_hash_ar.h | 4 ++-- intl/msg_hash_chs.h | 2 +- intl/msg_hash_cht.h | 2 +- intl/msg_hash_eo.h | 2 +- intl/msg_hash_nl.h | 2 +- intl/msg_hash_us.h | 6 +++--- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index 3b3a300812..c9de17a852 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -460,7 +460,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, - "Achievements Hardcore Mode" + "Hardcore Mode" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_LEADERBOARDS_ENABLE, @@ -500,7 +500,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_VERBOSE_ENABLE, - "Achievements Verbose Mode" + "Verbose Mode" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CLOSE_CONTENT, diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index 425fdd8e12..e52c4924b3 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -413,7 +413,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, -/* FIXME? Translate 'Achievements Hardcore Mode' */ +/* FIXME? Translate 'Hardcore Mode' */ "专家模式" ) MSG_HASH( diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index f89f7bc672..58304ec5ad 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -413,7 +413,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, -/* FIXME? Translate 'Achievements Hardcore Mode' */ +/* FIXME? Translate 'Hardcore Mode' */ "成就-專家模式" ) MSG_HASH( diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 48e9056dcd..439d0771ff 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -345,7 +345,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, - "Achievements Hardcore Mode" + "Hardcore Mode" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ACHIEVEMENTS, diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index 73bcdb3c86..583b964f47 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -345,7 +345,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, - "Achievements Hardcore Mode" + "Hardcore Mode" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ACHIEVEMENTS, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 7e8f235a75..9b94e3ecba 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -460,7 +460,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, - "Achievements Hardcore Mode" + "Hardcore Mode" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_LEADERBOARDS_ENABLE, @@ -500,11 +500,11 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_VERBOSE_ENABLE, - "Achievements Verbose Mode" + "Verbose Mode" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_AUTO_SCREENSHOT, - "Achievements Automatic Screenshot" + "Automatic Screenshot" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CLOSE_CONTENT, From 8ecba8b2a3f67c48db4629e5eb645ee6aab6705b Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Sun, 25 Mar 2018 18:40:22 +0200 Subject: [PATCH 003/517] MUI New dpi calculation. --- menu/menu_driver.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/menu/menu_driver.c b/menu/menu_driver.c index afe4010343..c335abad97 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -536,20 +536,19 @@ void menu_display_unset_framebuffer_dirty_flag(void) * RGUI or XMB use this. */ float menu_display_get_dpi(void) { - gfx_ctx_metrics_t metrics; settings_t *settings = config_get_ptr(); - float dpi = menu_dpi_override_value; + float dpi; + unsigned width, height; + + video_driver_get_size(&width, &height); if (!settings) return true; - metrics.type = DISPLAY_METRIC_DPI; - metrics.value = &dpi; + dpi = sqrt((width * width) + (height * height)) / 6.5; if (settings->bools.menu_dpi_override_enable) return settings->uints.menu_dpi_override_value; - else if (!video_context_driver_get_metrics(&metrics) || !dpi) - return menu_dpi_override_value; return dpi; } From 97ecba8575bf268c06af2f5963219c786beeaa73 Mon Sep 17 00:00:00 2001 From: aliaspider Date: Mon, 26 Mar 2018 17:44:08 +0100 Subject: [PATCH 004/517] D3D11: hwrender: add support for cache_context. --- gfx/drivers/d3d11.c | 47 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index a33fa75ad1..8ee67d0c10 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -39,6 +39,11 @@ #include "../drivers_shader/slang_process.h" #endif +static D3D11Device cached_device; +static D3D_FEATURE_LEVEL cached_supportedFeatureLevel; +static D3D11DeviceContext cached_context; + + #ifdef HAVE_OVERLAY static void d3d11_free_overlays(d3d11_video_t* d3d11) { @@ -548,8 +553,17 @@ static void d3d11_gfx_free(void* data) font_driver_free_osd(); - Release(d3d11->context); - Release(d3d11->device); + if (video_driver_is_video_cache_context()) + { + cached_device = d3d11->device; + cached_context = d3d11->context; + cached_supportedFeatureLevel = d3d11->supportedFeatureLevel; + } + else + { + Release(d3d11->context); + Release(d3d11->device); + } win32_monitor_from_window(); win32_destroy_window(); @@ -618,11 +632,32 @@ d3d11_gfx_init(const video_info_t* video, const input_driver_t** input, void** i #ifdef DEBUG flags |= D3D11_CREATE_DEVICE_DEBUG; #endif + if(cached_device && cached_context) + { + IDXGIFactory* dxgiFactory = NULL; + IDXGIDevice* dxgiDevice = NULL; + IDXGIAdapter* adapter = NULL; - D3D11CreateDeviceAndSwapChain( - NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, &requested_feature_level, 1, - D3D11_SDK_VERSION, &desc, (IDXGISwapChain**)&d3d11->swapChain, &d3d11->device, - &d3d11->supportedFeatureLevel, &d3d11->context); + d3d11->device = cached_device; + d3d11->context = cached_context; + d3d11->supportedFeatureLevel = cached_supportedFeatureLevel; + + d3d11->device->lpVtbl->QueryInterface(d3d11->device, uuidof(IDXGIDevice), (void**)&dxgiDevice); + dxgiDevice->lpVtbl->GetAdapter(dxgiDevice, &adapter); + adapter->lpVtbl->GetParent(adapter, uuidof(IDXGIFactory1), (void**)&dxgiFactory); + dxgiFactory->lpVtbl->CreateSwapChain(dxgiFactory, (IUnknown*)d3d11->device, &desc, (IDXGISwapChain**)&d3d11->swapChain); + + dxgiFactory->lpVtbl->Release(dxgiFactory); + adapter->lpVtbl->Release(adapter); + dxgiDevice->lpVtbl->Release(dxgiDevice); + } + else + { + D3D11CreateDeviceAndSwapChain( + NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, &requested_feature_level, 1, + D3D11_SDK_VERSION, &desc, (IDXGISwapChain**)&d3d11->swapChain, &d3d11->device, + &d3d11->supportedFeatureLevel, &d3d11->context); + } } { From 923ab3b6f4cf34e055f18b0d159270831db8935b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 27 Mar 2018 14:19:36 +0200 Subject: [PATCH 005/517] (DRM Ctx) Buildfix --- gfx/drivers_context/drm_ctx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c index 90fadbba4f..2300815c18 100644 --- a/gfx/drivers_context/drm_ctx.c +++ b/gfx/drivers_context/drm_ctx.c @@ -903,6 +903,7 @@ static void gfx_ctx_drm_set_flags(void *data, uint32_t flags) const gfx_ctx_driver_t gfx_ctx_drm = { gfx_ctx_drm_init, gfx_ctx_drm_destroy, + gfx_ctx_drm_get_api, gfx_ctx_drm_bind_api, gfx_ctx_drm_swap_interval, gfx_ctx_drm_set_video_mode, From 1cfc8a5fc8c3dbd6a51f29f0bcc3906f868ad988 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 27 Mar 2018 17:03:36 +0200 Subject: [PATCH 006/517] (Coverity) Fix some memory leaks --- menu/cbs/menu_cbs_ok.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index da369491e7..3f1915decc 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -1457,6 +1457,8 @@ static int action_ok_playlist_entry_collection(const char *path, runloop_msg_queue_push( "File could not be loaded from playlist.\n", 1, 100, true); + if (playlist_initialized) + playlist_free(tmp_playlist); return menu_cbs_exit(); } @@ -1625,13 +1627,18 @@ static int action_ok_playlist_entry_start_content(const char *path, if (!playlist || !menu_content_playlist_load(playlist, selection_ptr)) { runloop_msg_queue_push("File could not be loaded from playlist.\n", 1, 100, true); - return menu_cbs_exit(); + goto error; } playlist_get_index(playlist, selection_ptr, &path, NULL, NULL, NULL, NULL, NULL); return default_action_ok_load_content_from_playlist_from_menu(core_path, path, entry_label); + +error: + if (playlist_initialized) + playlist_free(tmp_playlist); + return menu_cbs_exit(); } static int action_ok_lookup_setting(const char *path, From d8e382c585f886f8d951a022feae01c7b88294dc Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 28 Mar 2018 01:28:15 +0200 Subject: [PATCH 007/517] (Cheevos) Take out cheevos support for MSVC 2010 again, CORO_YIELD macro does not work with MSVC 2010 --- pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj b/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj index 5855cd82fc..1b343be139 100644 --- a/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj +++ b/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj @@ -182,7 +182,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -201,7 +201,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -221,7 +221,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -240,7 +240,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -262,7 +262,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -287,7 +287,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -313,7 +313,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -338,7 +338,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp From 51f0e6f9d35c21bbae85ee2848b0749b6b38087f Mon Sep 17 00:00:00 2001 From: meleu Date: Wed, 28 Mar 2018 00:30:01 -0300 Subject: [PATCH 008/517] CHANGES: cheevos for NeoGeo, CPS1, CPS2 and CPS3 The fbalpha core supports cheevos for these drivers. Thanks to @barbudreadmon for adding this support ;) --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index cc165987b8..f1add95408 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ - ANDROID/OPENSL: Prevent crashes when setting audio latency too low (buffer count can never be lower than 2 now). - COMMON: Add way to reset core association for playlist entry. - COMMON: Add OSD statistics for video/audio/core. -- CHEEVOS: Support Atari 2600, Virtual Boy, Neo Geo (Arcade). +- CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. - LIBRETRO: Addition - Functions to enable and disable audio and video, and an environment function to query status of audio and video enables. From bb8638798658f22fe12358e442c3f6b9e06e8117 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 28 Mar 2018 14:56:22 +0200 Subject: [PATCH 009/517] Re-enable HAVE_CHEEVOS for MSVC 2010 --- pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj b/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj index 1b343be139..7f46f09291 100644 --- a/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj +++ b/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj @@ -182,12 +182,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions + OldStyle CONSOLE @@ -201,12 +202,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions + OldStyle CONSOLE @@ -221,12 +223,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions2 + OldStyle CONSOLE @@ -240,12 +243,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions2 + OldStyle CONSOLE @@ -262,13 +266,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions true + OldStyle WINDOWS @@ -287,13 +292,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions true + OldStyle WINDOWS @@ -313,13 +319,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions2 true + OldStyle WINDOWS @@ -338,13 +345,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions2 true + OldStyle WINDOWS From 167b977c4df71dddb8d7f9b946936222fe378285 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 28 Mar 2018 16:22:35 +0200 Subject: [PATCH 010/517] Fix some build errors with MSVC 2003/2005 cores --- frontend/drivers/platform_unix.c | 13 ++++++++++--- libretro-common/compat/compat_snprintf.c | 1 - libretro-common/compat/fopen_utf8.c | 3 ++- libretro-common/file/config_file.c | 2 +- libretro-common/include/compat/fopen_utf8.h | 4 +--- libretro-common/vfs/vfs_implementation.c | 2 +- verbosity.c | 2 +- 7 files changed, 16 insertions(+), 11 deletions(-) diff --git a/frontend/drivers/platform_unix.c b/frontend/drivers/platform_unix.c index b750f43c66..338200cc0d 100644 --- a/frontend/drivers/platform_unix.c +++ b/frontend/drivers/platform_unix.c @@ -2372,8 +2372,15 @@ static bool frontend_unix_check_for_path_changes(path_change_data_t *change_data { unsigned j; - /* A successful close does not guarantee that the data has been successfully saved to disk, as the kernel defers writes. It is not common for a file system to flush the buffers when the stream is closed. - * So we manually fsync() here to flush the data to disk, to make sure that the new data is immediately available when the file is re-read. + /* A successful close does not guarantee that the + * data has been successfully saved to disk, + * as the kernel defers writes. It is + * not common for a file system to flush + * the buffers when the stream is closed. + * + * So we manually fsync() here to flush the data + * to disk, to make sure that the new data is + * immediately available when the file is re-read. */ for (j = 0; j < inotify_data->wd_list->count; j++) { @@ -2381,7 +2388,7 @@ static bool frontend_unix_check_for_path_changes(path_change_data_t *change_data { /* found the right file, now sync it */ const char *path = inotify_data->path_list->elems[j].data; - FILE *fp = fopen_utf8(path, "rb"); + FILE *fp = (FILE*)fopen_utf8(path, "rb"); RARCH_LOG("file change detected: %s\n", path); diff --git a/libretro-common/compat/compat_snprintf.c b/libretro-common/compat/compat_snprintf.c index 03bfba64da..f31de72d8c 100644 --- a/libretro-common/compat/compat_snprintf.c +++ b/libretro-common/compat/compat_snprintf.c @@ -25,7 +25,6 @@ #include -#include #include #if _MSC_VER < 1800 diff --git a/libretro-common/compat/fopen_utf8.c b/libretro-common/compat/fopen_utf8.c index abd50cf275..d9fe35fec6 100644 --- a/libretro-common/compat/fopen_utf8.c +++ b/libretro-common/compat/fopen_utf8.c @@ -1,5 +1,6 @@ #include #include +#include #include #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) @@ -11,7 +12,7 @@ #ifdef _WIN32 #undef fopen -FILE* fopen_utf8(const char * filename, const char * mode) +void *fopen_utf8(const char * filename, const char * mode) { #if defined(_XBOX) return fopen(filename, mode); diff --git a/libretro-common/file/config_file.c b/libretro-common/file/config_file.c index 455d3aa513..50bdfd8e30 100644 --- a/libretro-common/file/config_file.c +++ b/libretro-common/file/config_file.c @@ -862,7 +862,7 @@ bool config_file_write(config_file_t *conf, const char *path) if (!string_is_empty(path)) { void* buf = NULL; - FILE *file = fopen_utf8(path, "wb"); + FILE *file = (FILE*)fopen_utf8(path, "wb"); if (!file) return false; diff --git a/libretro-common/include/compat/fopen_utf8.h b/libretro-common/include/compat/fopen_utf8.h index 67cc289aa6..1fe6a8aab6 100644 --- a/libretro-common/include/compat/fopen_utf8.h +++ b/libretro-common/include/compat/fopen_utf8.h @@ -1,13 +1,11 @@ #ifndef __FOPEN_UTF8_H #define __FOPEN_UTF8_H -#include - #ifdef _WIN32 /* defined to error rather than fopen_utf8, to make it clear to everyone reading the code that not worrying about utf16 is fine */ /* TODO: enable */ /* #define fopen (use fopen_utf8 instead) */ -FILE* fopen_utf8(const char * filename, const char * mode); +void *fopen_utf8(const char * filename, const char * mode); #else #define fopen_utf8 fopen #endif diff --git a/libretro-common/vfs/vfs_implementation.c b/libretro-common/vfs/vfs_implementation.c index 95fbbf1d8d..ce6fa5bf0e 100644 --- a/libretro-common/vfs/vfs_implementation.c +++ b/libretro-common/vfs/vfs_implementation.c @@ -264,7 +264,7 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, uns if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) { - FILE *fp = fopen_utf8(path, mode_str); + FILE *fp = (FILE*)fopen_utf8(path, mode_str); if (!fp) goto error; diff --git a/verbosity.c b/verbosity.c index 4f3548af97..ddb46512a9 100644 --- a/verbosity.c +++ b/verbosity.c @@ -101,7 +101,7 @@ void retro_main_log_file_init(const char *path) if (path == NULL) return; - log_file_fp = fopen_utf8(path, "wb"); + log_file_fp = (FILE*)fopen_utf8(path, "wb"); log_file_initialized = true; /* TODO: this is only useful for a few platforms, find which and add ifdef */ From 5a7c37b81968e18e868a0b6db14e69b8b79e8654 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 28 Mar 2018 16:26:40 +0200 Subject: [PATCH 011/517] Revert "(OpenSL) Fix issues with cores using threaded audio - audio thread" This reverts commit 54708a944a59d565bb38d4df03f0617e188219f8. --- audio/drivers/opensl.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/audio/drivers/opensl.c b/audio/drivers/opensl.c index 3e4de301cc..d1ae7446a5 100644 --- a/audio/drivers/opensl.c +++ b/audio/drivers/opensl.c @@ -203,9 +203,6 @@ error: static bool sl_stop(void *data) { sl_t *sl = (sl_t*)data; - - opensl_callback((SLAndroidSimpleBufferQueueItf) 0, data); - sl->is_paused = (SLPlayItf_SetPlayState(sl->player, SL_PLAYSTATE_STOPPED) == SL_RESULT_SUCCESS) ? true : false; From d17eee9cca533bda3f6fd18470558a123878ead4 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 28 Mar 2018 16:33:19 +0200 Subject: [PATCH 012/517] We can actually disable DPI override by default now --- config.def.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config.def.h b/config.def.h index e7c29ab786..829ea18345 100644 --- a/config.def.h +++ b/config.def.h @@ -601,11 +601,7 @@ static const unsigned default_content_history_size = 100; /* Show Menu start-up screen on boot. */ static const bool default_menu_show_start_screen = true; -#ifdef RARCH_MOBILE static const bool menu_dpi_override_enable = false; -#else -static const bool menu_dpi_override_enable = true; -#endif #ifdef RARCH_MOBILE static const unsigned menu_dpi_override_value = 72; From 89e912087bf7f15e302a2c14fef567c9a0bd98d5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 28 Mar 2018 16:41:37 +0200 Subject: [PATCH 013/517] Put RARCH_INTERNAL ifdef around this --- libretro-common/file/file_path.c | 2 +- libretro-common/include/file/file_path.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index 4830898775..df129dbb7c 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -1107,7 +1107,7 @@ void path_basedir_wrapper(char *path) snprintf(path, 3, ".%s", path_default_slash()); } -#if !defined(RARCH_CONSOLE) +#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) void fill_pathname_application_path(char *s, size_t len) { size_t i; diff --git a/libretro-common/include/file/file_path.h b/libretro-common/include/file/file_path.h index 85f190c6ad..4a29494d98 100644 --- a/libretro-common/include/file/file_path.h +++ b/libretro-common/include/file/file_path.h @@ -454,7 +454,7 @@ void path_basedir_wrapper(char *path); **/ void fill_pathname_slash(char *path, size_t size); -#ifndef RARCH_CONSOLE +#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) void fill_pathname_application_path(char *buf, size_t size); #endif From e71f56d4a31747fc0d097966ee27023175aad6c5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 28 Mar 2018 16:45:05 +0200 Subject: [PATCH 014/517] Update libretro-common --- libretro-common/file/file_path.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index df129dbb7c..880153ffc8 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -793,7 +793,7 @@ bool path_is_absolute(const char *path) **/ void path_resolve_realpath(char *buf, size_t size) { -#ifndef RARCH_CONSOLE +#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) char tmp[PATH_MAX_LENGTH]; tmp[0] = '\0'; @@ -971,7 +971,7 @@ void fill_short_pathname_representation_noext(char* out_rep, void fill_pathname_expand_special(char *out_path, const char *in_path, size_t size) { -#if !defined(RARCH_CONSOLE) +#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) if (*in_path == '~') { const char *home = getenv("HOME"); @@ -1020,7 +1020,7 @@ void fill_pathname_expand_special(char *out_path, void fill_pathname_abbreviate_special(char *out_path, const char *in_path, size_t size) { -#if !defined(RARCH_CONSOLE) +#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) unsigned i; const char *candidates[3]; const char *notations[3]; From d4558774dfd0df88cc317b9eab25d3f46a6ec677 Mon Sep 17 00:00:00 2001 From: alfrix Date: Wed, 28 Mar 2018 11:55:20 -0300 Subject: [PATCH 015/517] Update Polish translation (DEX357) --- CHANGES.md | 3 +- intl/msg_hash_pl.h | 474 +++++++++++++++++++++++++++------------------ 2 files changed, 289 insertions(+), 188 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cc165987b8..49c755415c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. - LIBRETRO: Addition - Functions to enable and disable audio and video, and an environment function to query status of audio and video enables. - LOCALIZATION: Update Italian translation. +- LOCALIZATION: Update Polish translation. - MENU: Disable XMB shadow icons by default for PowerPC and ARM for performance reasons. - MENU/XMB: Fixed left/right tab regression. - MENU/XMB: Fix scaling of tall images that were cut on bottom previously. @@ -309,7 +310,7 @@ Skipped this one. - MISC: Various frontend optimizations. - NET: Fix bug #4703 (https://github.com/libretro/RetroArch/issues/4703) - OSX/MACOS: Fixes serious memory leak -- THUMBNAILS: Thumbnails show up now in Load Content -> Collection, Information -> Database +- THUMBNAILS: Thumbnails show up now in Load Content -> Collection, Information -> Database - VIDEO: Fix threaded video regression; tickering of menu entries would no longer work. - VITA: Fix 30fps menu (poke into input now instead of reading the entire input buffer which apparently is slow) - VITA: Fix frame throttle diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index b26d250b0b..40167dd3d5 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -12,7 +12,7 @@ MSG_HASH( ) MSG_HASH( MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED, - "Otrzymano nieznane polecenie netplay" + "Otrzymano nieznane polecenie gry internetwoej" ) MSG_HASH( MSG_FILE_ALREADY_EXISTS_SAVING_TO_BACKUP_BUFFER, @@ -50,17 +50,53 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Dołączyłeś jako gracz %u" ) +MSG_HASH( + MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S, + "Dołączyłeś do urządzeń wejściowych %.*s" + ) +MSG_HASH( + MSG_NETPLAY_PLAYER_S_LEFT, + "Gracz %.*s opuścił grę" + ) +MSG_HASH( + MSG_NETPLAY_S_HAS_JOINED_AS_PLAYER_N, + "%.*s dołączył jako gracz %u" + ) +MSG_HASH( + MSG_NETPLAY_S_HAS_JOINED_WITH_INPUT_DEVICES_S, + "%.*s dołączył do urządzeń wejściowych %.*s" + ) +MSG_HASH( + MSG_NETPLAY_NOT_RETROARCH, + "Próba połączenia online nie powiodła się, ponieważ peer nie działa w trybie RetroArch lub działa w starej wersji RetroArch." + ) +MSG_HASH( + MSG_NETPLAY_OUT_OF_DATE, + "Grający online, peer ma starą wersję RetroArch. Nie można połączyć." + ) +MSG_HASH( + MSG_NETPLAY_DIFFERENT_VERSIONS, + "OSTRZEŻENIE: Grający w online ma inną wersję RetroArch. Jeśli wystąpią problemy, użyj tej samej wersji." + ) +MSG_HASH( + MSG_NETPLAY_DIFFERENT_CORES, + "Grający w online ma inny rdzeń. Nie można połączyć." + ) +MSG_HASH( + MSG_NETPLAY_DIFFERENT_CORE_VERSIONS, + "OSTRZEŻENIE: Grający w online ma inną wersję rdzenia. Jeśli wystąpią problemy, użyj tej samej wersji." + ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, - "Ten rdzeń nie obsługuje net-play między architekturami w tych systemach" + "Ten rdzeń nie obsługuje gry internetwoej między architekturami w tych systemach" ) MSG_HASH( MSG_NETPLAY_PLATFORM_DEPENDENT, - "Ten rdzeń nie obsługuje netplay między architekturami" + "Ten rdzeń nie obsługuje gry online między architekturami" ) MSG_HASH( MSG_NETPLAY_ENTER_PASSWORD, - "Wprowadź hasło do serwera netplay:" + "Wprowadź hasło do serwera gry online:" ) MSG_HASH( MSG_NETPLAY_INCORRECT_PASSWORD, @@ -72,11 +108,11 @@ MSG_HASH( ) MSG_HASH( MSG_NETPLAY_SERVER_HANGUP, - "Klient netplay został odłączony" + "Klient gry online został odłączony" ) MSG_HASH( MSG_NETPLAY_CLIENT_HANGUP, - "Netplay odłączony" + "Gracz online odłączony" ) MSG_HASH( MSG_NETPLAY_CANNOT_PLAY_UNPRIVILEGED, @@ -86,13 +122,17 @@ MSG_HASH( MSG_NETPLAY_CANNOT_PLAY_NO_SLOTS, "Nie ma wolnych miejsc dla graczy" ) +MSG_HASH( + MSG_NETPLAY_CANNOT_PLAY_NOT_AVAILABLE, + "Żądane urządzenia wejściowe nie są dostępne" + ) MSG_HASH( MSG_NETPLAY_CANNOT_PLAY, "Nie można przełączyć do trybu odtwarzania" ) MSG_HASH( MSG_NETPLAY_PEER_PAUSED, - "Netplay \"%s\" wstrzymano" + "Gra online \"%s\" wstrzymano" ) MSG_HASH( MSG_NETPLAY_CHANGED_NICK, @@ -108,11 +148,11 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC, - "Hard-synchronize CPU i GPU. Zmniejsza opóźnienie kosztem wydajności." + "Trudna synchronizacja CPU i GPU. Zmniejsza opóźnienie kosztem wydajności." ) MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_THREADED, - "Poprawia wydajność kosztem opóźnień i częstszego rwania wideo. Używaj tylko wtedy, gdy nie możesz uzyskać pełnej prędkości w przeciwnym razie." + "Poprawia wydajność kosztem opóźnień i częstszego rwania obrazu. Używaj tylko wtedy, gdy nie możesz uzyskać pełnej prędkości w przeciwnym razie." ) MSG_HASH( MSG_AUDIO_VOLUME, @@ -192,7 +232,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_NETPLAY_TAB, - "Pokoje Netplay" + "Pokoje gry online" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_ASK_ARCHIVE, @@ -280,7 +320,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_AUTOSAVE_INTERVAL, - "SaveRAM Autozapis Interwału" + "Autozapis SaveRAM" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_AUTO_OVERRIDES_ENABLE, @@ -292,7 +332,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_AUTO_SHADERS_ENABLE, - "Automatycznie załaduj Presety Shadera" + "Automatycznie załaduj Shadery" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_BACK, @@ -304,7 +344,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_INFO, - "Info" + "Informacje" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_QUIT, @@ -336,11 +376,11 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_CONFIRM, - "Potwierdz/OK" + "Potwierdź/OK" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_INFO, - "Info" + "Informacje" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_QUIT, @@ -368,7 +408,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_BLUETOOTH_ENABLE, - "Bluetooth Włącz" + "Włącz Bluetooth" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_BUILDBOT_ASSETS_URL, @@ -396,11 +436,11 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEAT_DATABASE_PATH, - "Oszukane Pliki" + "Oszukane pliki" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEAT_FILE, - "Oszukane Pliki" + "Oszukane pliki" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEAT_FILE_LOAD, @@ -420,7 +460,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, - "Osiągnięcia Trybu Hardcore" + "Osiągnięcia trybu hardcore" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_LEADERBOARDS_ENABLE, @@ -460,7 +500,11 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_VERBOSE_ENABLE, - "Osiągnięcia trybu Pełnego" + "Osiągnięcia trybu pełnego" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CHEEVOS_AUTO_SCREENSHOT, + "Osiągnięcia automatyczne screenshot" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CLOSE_CONTENT, @@ -496,7 +540,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CONTENT_HISTORY_SIZE, - "Rozmiar listy historii") + "Rozmiar listy Historii") MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_ENTRY_REMOVE, "Zezwalaj na usuwanie wpisów") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SETTINGS, @@ -514,9 +558,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ENABLE, MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFORMATION, "Informacje podstawowe") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_AUTHORS, - "Autorski") + "Autor") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CATEGORIES, - "Kategorie") + "Kategoria") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_LABEL, "Etykieta rdzenia") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_NAME, @@ -524,7 +568,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_NAME, MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_FIRMWARE, "Firmware(s)") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_LICENSES, - "Licencje") + "Licencja") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_PERMISSIONS, "Uprawnienia") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_SUPPORTED_EXTENSIONS, @@ -532,7 +576,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_SUPPORTED_EXTENSIONS, MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_MANUFACTURER, "Producent systemu") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_NAME, - "Nawa systemu") + "Nazwa systemu") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INPUT_REMAPPING_OPTIONS, "Sterowanie") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_LIST, @@ -578,7 +622,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_NONE, MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_NOT_FOUND, "Nie znaleziono katalogu.") MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_SETTINGS, - "Informator") + "Informacja") MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_CYCLE_TRAY_STATUS, "Status tacy cyklowej dysku") MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_IMAGE_APPEND, @@ -588,7 +632,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_INDEX, MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_OPTIONS, "Kontrola dysku") MSG_HASH(MENU_ENUM_LABEL_VALUE_DONT_CARE, - "Nie przejmuj się") + "Brak") MSG_HASH(MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST, "Pliki do pobrania") MSG_HASH(MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE, @@ -638,11 +682,11 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE, MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP, "Pomoc") MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_AUDIO_VIDEO_TROUBLESHOOTING, - "Rozwiązywanie problemów audio / wideo") + "Rozwiązywanie problemów audio/wideo") MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_CHANGE_VIRTUAL_GAMEPAD, "Zmiana nakładki wirtualnego gamepada") MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_CONTROLS, - "Podstawowe kontrolki menu") + "Podstawowa kontrola menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_LIST, "Pomoc") MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_LOADING_CONTENT, @@ -668,57 +712,57 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ADC_TYPE, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ALL_USERS_CONTROL_MENU, "Menu sterowania wszystkich użytkowników") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X, - "Lewy Analog X") + "Lewy analog X") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_MINUS, - "Lewy analog X - (lewy)") + "Lewy analog X- (lewy)") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_PLUS, - "Lewy analog X + (po prawej)") + "Lewy analog X+ (po prawej)") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y, - "Left Analog Y") + "Lewy analog Y") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_MINUS, - "Lewy analogo Y- (w górę)") + "Lewy analog Y- (w górę)") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_PLUS, - "Lewy analogo Y + (dół)") + "Lewy analog Y+ (dół)") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X, - "Prawo Analog X") + "Prawo analog X") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X_MINUS, "Prawy analog X- (po lewej)") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X_PLUS, - "Prawy analog X + (po prawej)") + "Prawy analog X+ (po prawej)") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y, "Prawo analog Y") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_MINUS, "Prawy analog Y- (w górę)") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_PLUS, - "Prawe analog Y + (w dół)") + "Prawy analog Y+ (w dół)") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_TRIGGER, - "Spust broni") + "Spust pada") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_RELOAD, - "Przeładowanie broni") + "Przeładowanie pada") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_AUX_A, - "Aux A broni") + "Aux A") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_AUX_B, - "Aux B broni") + "Aux B") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_AUX_C, - "Gun Aux C") + "Aux C") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_START, "Start broni") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_SELECT, - "Wybierz broń") + "Wybierz") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_UP, - "D-pad góra broń") + "D-pad góra") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_DOWN, - "D-pad dół broń") + "D-pad dół") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_LEFT, - "D-pad lewo broń") + "D-pad lewo") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_RIGHT, - "D-pad prawo broń") + "D-pad prawo") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_AUTODETECT_ENABLE, - "Włącz Autoconfig") + "Włącz autoconfig") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_AXIS_THRESHOLD, "Martwa strefa gałki analogowej") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_INPUT_SWAP_OK_CANCEL, - "Zamień Menu OK i Anuluj przyciski") + "Zamień menu ok i anuluj przyciski") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_ALL, "Zwiąż wszystko") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_DEFAULT_ALL, @@ -738,7 +782,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_INDEX, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DRIVER, "Sterownik wejściowy") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DUTY_CYCLE, - "Cykl obciążenia") + "Cykl zapisu") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_HOTKEY_BINDS, "Wejściowe powiązania skrótów") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ICADE_ENABLE, @@ -828,7 +872,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_GRAB_MOUSE_TOGGLE, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, "Przełącznik ostrości gry") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_LOAD_STATE_KEY, - "Stan obciążenia") + "Wczytaj zapis") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, "Przełączanie menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, @@ -836,7 +880,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Przełącznik wyciszania dźwięku") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, - "Netplay przełącza tryb play / spectate") + "Netplay przełącza tryb play/spectate") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, "Przełączanie klawiatury ekranowej") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OVERLAY_NEXT, @@ -859,6 +903,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_PREV, "Poprzedni moduł cieniujący") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION_HOLD_KEY, "Zwolnione tempo") +MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION_KEY, + "Slow motion toggle") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, "Slot zapisu -") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS, @@ -874,7 +920,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_HIDE_IN_MENU, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS, "Pokaż nakładki na nakładce") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS_PORT, - "Pokaż wejścia Posłuchaj portu") + "Pokaż wejścia portu") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_POLL_TYPE_BEHAVIOR, "Zachowanie typu ankiety") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_POLL_TYPE_BEHAVIOR_EARLY, @@ -888,11 +934,11 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_PREFER_FRONT_TOUCH, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_REMAPPING_DIRECTORY, "Odwzorowanie wejścia") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_REMAP_BINDS_ENABLE, - "Włącz Remap Binds") + "Włącz Remap Powiązań") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_SAVE_AUTOCONFIG, "Zapisz Autoconfig") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_SETTINGS, - "Wkład") + "Sterowanie") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_SMALL_KEYBOARD_ENABLE, "Włącz małą klawiaturę") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_TOUCH_ENABLE, @@ -902,7 +948,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_TURBO_ENABLE, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_TURBO_PERIOD, "Okres Turbo") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_USER_BINDS, - "Wprowadź użytkownika %u Binds") + "Wprowadź powiązania użytkownika %u") MSG_HASH(MENU_ENUM_LABEL_VALUE_INTERNAL_STORAGE_STATUS, "Status wewnętrznej pamięci") MSG_HASH(MENU_ENUM_LABEL_VALUE_JOYPAD_AUTOCONFIG_DIR, @@ -912,7 +958,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_JOYPAD_DRIVER, MSG_HASH(MENU_ENUM_LABEL_VALUE_LAKKA_SERVICES, "Usługi") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_CHINESE_SIMPLIFIED, - "chiński (Simplified)") + "chiński (Uproszczony)") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_CHINESE_TRADITIONAL, "chiński (Tradycyjny)") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_DUTCH, @@ -922,7 +968,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ENGLISH, MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ESPERANTO, "esperanto") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_FRENCH, - "Francuski") + "francuski") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_GERMAN, "niemiecki") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ITALIAN, @@ -938,15 +984,15 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_PORTUGUESE_BRAZIL, MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_PORTUGUESE_PORTUGAL, "portugalski (portugalia)") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_RUSSIAN, - "Russianrosyjski") + "rosyjski") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_SPANISH, - "Spanish") + "hiszpański") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_VIETNAMESE, "wietnamski") MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ARABIC, - "Arabic") + "arabski") MSG_HASH(MENU_ENUM_LABEL_VALUE_LEFT_ANALOG, - "Lewy analogowy") + "Lewy analog") MSG_HASH(MENU_ENUM_LABEL_VALUE_LIBRETRO_DIR_PATH, "Rdzeń") MSG_HASH(MENU_ENUM_LABEL_VALUE_LIBRETRO_INFO_PATH, @@ -962,7 +1008,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_HISTORY, MSG_HASH(MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_LIST, "Załaduj zawartość") MSG_HASH(MENU_ENUM_LABEL_VALUE_LOAD_STATE, - "Stan obciążenia") + "Wczytaj zapis") MSG_HASH(MENU_ENUM_LABEL_VALUE_LOCATION_ALLOW, "Zezwalaj na lokalizację") MSG_HASH(MENU_ENUM_LABEL_VALUE_LOCATION_DRIVER, @@ -1028,25 +1074,25 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NAVIGATION_WRAPAROUND, MSG_HASH(MENU_ENUM_LABEL_VALUE_NEAREST, "Najbliższy") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY, - "Netplay") + "Gra online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ALLOW_SLAVES, "Zezwalaj na klientów w trybie slave") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES, - "Netplay sprawdź klatki") + "Sprawdź klatki gry online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "Wejściowe klatki opóźnień") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Zakres latencji wejściowych klatek") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, - "Netplay opóźnij klatki") + "Opóźnij klatki gry online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, - "Odłącz od hosta netplay") + "Odłącz od hosta gry online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE, - "Włącz Netplay") + "Włącz gre online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE_CLIENT, - "Połącz się z hostem netplay") + "Połącz się z hostem gry online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE_HOST, - "Uruchom hosta netplay") + "Uruchom hosta gry online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISABLE_HOST, "Zatrzymaj hosta gry") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_IP_ADDRESS, @@ -1054,29 +1100,49 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_IP_ADDRESS, MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_LAN_SCAN_SETTINGS, "Zeskanuj sieć lokalną") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_MODE, - "Włącz klienta Netplay") + "Włącz klienta gry online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_NICKNAME, "Nazwa Użytkownika") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD, "Hasło serwera") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE, - "Publicznie ogłosić grę na Netplay") + "Publicznie ogłosić grę na online") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUEST_DEVICE_I, + "Zażądaj urządzenia %u") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUIRE_SLAVES, "Odmów klientów w trybie innym niż slave") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS, - "Ustawienia Netplay") + "Ustawienia gry online") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG, + "Udostępnianie wejścia analogowego") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_MAX, + "Max") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_AVERAGE, + "Średni") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL, + "Udostępnianie wejścia cyfrowego") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_OR, + "Podziel") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_XOR, + "Zahacz") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_VOTE, + "Głosuj") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NONE, + "Żaden") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NO_PREFERENCE, + "Bez preferencji") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR, - "Tryb Netplay Spectator") + "Tryb widza gry online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_STATELESS_MODE, - "Tryb bezstanowej Netplay") + "Tryb bezstanowej gry online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SPECTATE_PASSWORD, "Hasło spontaniczne serwera") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SPECTATOR_MODE_ENABLE, - "Netplay Spectator Włączone") + "Widz gry online Włączone") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_TCP_UDP_PORT, - "Port TCP Netplay") + "Port TCP gry online") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_NAT_TRAVERSAL, - "Netplay NAT Traversal") + "Gra online NAT Traversal") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_ENABLE, "Polecenia sieciowe") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_PORT, @@ -1112,7 +1178,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_HISTORY_AVAILABLE, MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE, "Brak informacji.") MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_ITEMS, - "Nie ma przedmiotów.") + "Nie ma plików.") MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_NETPLAY_HOSTS_FOUND, "Nie znaleziono hostów netplay.") MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_NETWORKS_FOUND, @@ -1128,13 +1194,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND, MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_SHADER_PARAMETERS, "Brak parametrów modułu cieniującego.") MSG_HASH(MENU_ENUM_LABEL_VALUE_OFF, - "OFF") + "Wyłącz") MSG_HASH(MENU_ENUM_LABEL_VALUE_ON, - "ON") + "Włącz") MSG_HASH(MENU_ENUM_LABEL_VALUE_ONLINE, "Online") MSG_HASH(MENU_ENUM_LABEL_VALUE_ONLINE_UPDATER, - "Online Updater") + "Aktualizacja online") MSG_HASH(MENU_ENUM_LABEL_VALUE_ONSCREEN_DISPLAY_SETTINGS, "Wyświetlacz na ekranie") MSG_HASH(MENU_ENUM_LABEL_VALUE_ONSCREEN_OVERLAY_SETTINGS, @@ -1162,7 +1228,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_SETTINGS, MSG_HASH(MENU_ENUM_LABEL_VALUE_PAL60_ENABLE, "Użyj trybu PAL60") MSG_HASH(MENU_ENUM_LABEL_VALUE_PARENT_DIRECTORY, - "Nadrzędna Lokalizacja") + "Nadrzędny katalog") MSG_HASH(MENU_ENUM_LABEL_VALUE_PAUSE_LIBRETRO, "Wstrzymaj przy włączonym menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_PAUSE_NONACTIVE, @@ -1176,7 +1242,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_DIRECTORY, MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_SETTINGS, "Listy odtwarzania") MSG_HASH(MENU_ENUM_LABEL_VALUE_POINTER_ENABLE, - "Dotknij Wsparcie") + "Wsparcie dotyku") MSG_HASH(MENU_ENUM_LABEL_VALUE_PORT, "Port") MSG_HASH(MENU_ENUM_LABEL_VALUE_PRESENT, @@ -1256,7 +1322,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_DRIVER, MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_ENABLE, "Włącz nagrywanie") MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_PATH, - "Zapisz zapisywanie wyjścia jako...") + "Zapisz wyjścia jako...") MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_USE_OUTPUT_DIRECTORY, "Zapisz nagrania w katalogu wyjściowym") MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE, @@ -1286,7 +1352,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROKEYBOARD, MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROPAD, "Retro pad") MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROPAD_WITH_ANALOG, - "Retro pad w/ Analog") + "Retro pad w/Analog") MSG_HASH(MENU_ENUM_LABEL_VALUE_RETRO_ACHIEVEMENTS_SETTINGS, "Osiągnięcia") MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_ENABLE, @@ -1307,6 +1373,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_FAVORITES, "Dodaj do ulubionych") MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_FAVORITES_PLAYLIST, "Dodaj do ulubionych") +MSG_HASH(MENU_ENUM_LABEL_VALUE_RESET_CORE_ASSOCIATION, + "Resetuj podstawowy rdzeń") MSG_HASH(MENU_ENUM_LABEL_VALUE_RUN, "Biec") MSG_HASH(MENU_ENUM_LABEL_VALUE_RUN_MUSIC, @@ -1324,7 +1392,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATE_AUTO_SAVE, MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATE_DIRECTORY, "Zapisz stan") MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATE_THUMBNAIL_ENABLE, - "Zapisz Miniatury") + "Zapisz miniatury") MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG, "Zapisz bieżącą konfigurację") MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG_OVERRIDE_CORE, @@ -1334,7 +1402,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG_OVERRIDE_GAME, MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_NEW_CONFIG, "Zapisz nową konfigurację") MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_STATE, - "Zapisz stans") + "Zapisz stan") MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVING_SETTINGS, "Zapisywanie") MSG_HASH(MENU_ENUM_LABEL_VALUE_SCAN_DIRECTORY, @@ -1378,9 +1446,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SHUTDOWN, MSG_HASH(MENU_ENUM_LABEL_VALUE_SLOWMOTION_RATIO, "Współczynnik powolnego ruchu") MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVEFILES_ENABLE, - "Sort Saves In Folders") + "Sortuj zapisy w folderach") MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVESTATES_ENABLE, - "Sortuj zapisz stany w folderach") + "Sortuj zapis w folderach") MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATES_IN_CONTENT_DIR_ENABLE, "Napisz zapis stanów do treści dir") MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVEFILES_IN_CONTENT_DIR_ENABLE, @@ -1398,7 +1466,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_START_NET_RETROPAD, MSG_HASH(MENU_ENUM_LABEL_VALUE_START_VIDEO_PROCESSOR, "Uruchom procesor wideo") MSG_HASH(MENU_ENUM_LABEL_VALUE_STATE_SLOT, - "Slot stanu") + "Slot zapisu") MSG_HASH(MENU_ENUM_LABEL_VALUE_STATUS, "Status") MSG_HASH(MENU_ENUM_LABEL_VALUE_STDIN_CMD_ENABLE, @@ -1446,7 +1514,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_EGL_SUPPORT, "Wsparcie EGL") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_FBO_SUPPORT, - "Obsługuje renderowanie do tekstury (shadery wieloprzebiegowe) OpenGL / Direct3D") + "Obsługuje renderowanie do tekstury (shadery wieloprzebiegowe) OpenGL/Direct3D") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_FFMPEG_SUPPORT, "Wsparcie FFmpeg") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_FREETYPE_SUPPORT, @@ -1466,7 +1534,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_HLSL_SUPPORT, MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_JACK_SUPPORT, "Obsługa JACK") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_KMS_SUPPORT, - "Obsługa KMS / EGL") + "Obsługa KMS/EGL") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LAKKA_VERSION, "Wersja Lakka") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LIBRETRODB_SUPPORT, @@ -1474,11 +1542,11 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LIBRETRODB_SUPPORT, MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LIBUSB_SUPPORT, "Wsparcie Libusb") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LIBXML2_SUPPORT, - "libxml2 Obsługa parsowania XML") + "libxml2 obsługa parowania XML") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_NETPLAY_SUPPORT, "Wsparcie Netplay (peer-to-peer)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_NETWORK_COMMAND_IFACE_SUPPORT, - "Obsługa interfejsu sieciowego Command") + "Obsługa interfejsu dowodzenia sieciowego") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_NETWORK_REMOTE_SUPPORT, "Obsługa sieciowego gamepada") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OPENAL_SUPPORT, @@ -1512,7 +1580,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_PYTHON_SUPPORT, MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RBMP_SUPPORT, "Obsługa BMP (RBMP)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RETRORATING_LEVEL, - "Poziom RetroRating") + "Poziom Oceny Retro") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RJPEG_SUPPORT, "Obsługa JPEG (RJPEG)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_ROARAUDIO_SUPPORT, @@ -1552,7 +1620,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_XVIDEO_SUPPORT, MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_ZLIB_SUPPORT, "Obsługa Zlib") MSG_HASH(MENU_ENUM_LABEL_VALUE_TAKE_SCREENSHOT, - "Zrobić zrzut ekranu") + "Zrób zrzut ekranu") MSG_HASH(MENU_ENUM_LABEL_VALUE_THREADED_DATA_RUNLOOP_ENABLE, "Zadania z wątkami") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS, @@ -1560,7 +1628,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS, MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS_DIRECTORY, "Miniatury") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS_UPDATER_LIST, - "Zaktualizuj Miniatury") + "Zaktualizuj miniatury") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_BOXARTS, "Boxarts") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_SCREENSHOTS, @@ -1568,7 +1636,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_SCREENSHOTS, MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_TITLE_SCREENS, "Ekrany tytułowe") MSG_HASH(MENU_ENUM_LABEL_VALUE_TIMEDATE_ENABLE, - "Pokaż datę / czas") + "Pokaż datę/czas") MSG_HASH(MENU_ENUM_LABEL_VALUE_TITLE_COLOR, "Kolor tytułu menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_TRUE, @@ -1588,7 +1656,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_UNDO_SAVE_STATE, MSG_HASH(MENU_ENUM_LABEL_VALUE_UNKNOWN, "Nieznany") MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATER_SETTINGS, - "Aktualizator") + "Aktualizacja") MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_ASSETS, "Zaktualizuj zasoby") MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_AUTOCONFIG_PROFILES, @@ -1658,7 +1726,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_ASPECT, MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_SRGB_DISABLE, "Wymuś wyłączenie sRGB FBO") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_DELAY, - "Opóźnienie ramki") + "Opóźnienie klatki") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FULLSCREEN, "Użyj trybu pełnoekranowego") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_GAMMA, @@ -1670,7 +1738,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_GPU_SCREENSHOT, MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC, "Trudna synchronizacja z GPU") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC_FRAMES, - "Twarde ramki do synchronizacji z GPU") + "Twarde klatki do synchronizacji z GPU") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MAX_SWAPCHAIN_IMAGES, "Maksymalne obrazy swapchain") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_POS_X, @@ -1684,7 +1752,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_POST_FILTER_RECORD, MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Odświeżanie w pionie") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, - "Szacunkowa liczba klatek na sekundę na ekranie") + "Szacowana liczba klatek na sekundę na ekranie") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Obrót") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1696,7 +1764,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SETTINGS, MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_DIR, "Moduł cieniujący wideo") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_NUM_PASSES, - "Shader Pass") + "Przepustka Shadera") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PARAMETERS, "Podgląd parametrów modułu cieniującego") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET, @@ -1705,6 +1773,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS, "Zapisz ustawienie Shadera jako") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_CORE, "Zapis ustawienia podstawowe rdzenia") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_PARENT, + "Zapisz ustawienie zawartości katalogu \"zawartości\"") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_GAME, "Zapisz ustawienie gry") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHARED_CONTEXT, @@ -1748,7 +1818,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_WIFI_DRIVER, MSG_HASH(MENU_ENUM_LABEL_VALUE_WIFI_SETTINGS, "Wi-Fi") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ALPHA_FACTOR, - "Menu Czynnik alfa") + "Menu czynnika alfa") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_FONT_COLOR_RED, + "Kolor czcionki czerwony") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_FONT_COLOR_GREEN, + "Kolor czcionki zielony") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_FONT_COLOR_BLUE, + "Kolor czcionki niebieski") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_FONT, "Czcionka menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_CUSTOM, @@ -1758,7 +1834,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_FLATUI, MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_MONOCHROME, "Monochromia") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_MONOCHROME_INVERTED, - "Monochromia Inverted") + "Odwrócona monochromia") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_SYSTEMATIC, "Systematyczny") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_NEOACTIVE, @@ -1777,6 +1853,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_APPLE_GREEN, "Zielone jabłko") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_DARK, "Ciemny") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_LIGHT, + "Jasny") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_MORNING_BLUE, + "Poranny błękit") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_DARK_PURPLE, "Ciemny fiolet") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_ELECTRIC_BLUE, @@ -1794,13 +1874,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_UNDERSEA, MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_VOLCANIC_RED, "Czerwień wulkaniczna") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_RIBBON_ENABLE, - "Menu Shader Pipeline") + "Animowany efekt tłą") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SCALE_FACTOR, "Współczynnik skali menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SHADOWS_ENABLE, "Włącz cienie ikony") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_HISTORY, - "Pokaż kartę historii") + "Pokaż kartę Historii") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_ADD, "Pokaż kartę Importuj zawartość") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_FAVORITES, @@ -1814,7 +1894,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS, MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Pokaż kartę Wideo") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, - "Pokaż kartę Netplay") + "Pokaż kartę Gry Online") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Motyw ikon menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -1824,7 +1904,7 @@ MSG_HASH(MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_TWO, MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_ENABLE, "Włącz lub wyłącz osiągnięcia. Aby uzyskać więcej informacji, odwiedź http://retroachievements.org") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, - "Włącz lub wyłącz nieoficjalne osiągnięcia i / lub funkcje beta do celów testowych.") + "Włącz lub wyłącz nieoficjalne osiągnięcia i/lub funkcje beta do celów testowych.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, "Włączanie i wyłączanie stanów save, cheats, rewind, fast forward, pause i slow-motion dla wszystkich gier.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_LEADERBOARDS_ENABLE, @@ -1833,6 +1913,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_BADGES_ENABLE, "Włącz lub wyłącz wyświetlanie znaczków na liście osiągnięć.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_VERBOSE_ENABLE, "Włącz lub wyłącz powiadomienia OSD dla osiągnięć.") +MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_AUTO_SCREENSHOT, + "Automatycznie wykonuj zrzut ekranu po uruchomieniu osiągnięcia.") MSG_HASH(MENU_ENUM_SUBLABEL_DRIVER_SETTINGS, "Zmień sterowniki używane przez system.") MSG_HASH(MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS, @@ -1888,9 +1970,9 @@ MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_USER_BINDS, MSG_HASH(MENU_ENUM_SUBLABEL_LOG_VERBOSITY, "Włącz lub wyłącz rejestrowanie w terminalu.") MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY, - "Dołącz lub obsługuj sesję netplay.") + "Dołącz lub obsługuj sesję gry online.") MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_LAN_SCAN_SETTINGS, - "Wyszukaj i połącz się z hostami netplay w sieci lokalnej.") + "Wyszukaj i połącz się z hostami gry online w sieci lokalnej.") MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST, "Wyświetl informacje o systemie.") MSG_HASH(MENU_ENUM_SUBLABEL_ONLINE_UPDATER, @@ -1900,7 +1982,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SAMBA_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SERVICES_SETTINGS, "Zarządzaj usługami na poziomie systemu operacyjnego.") MSG_HASH(MENU_ENUM_SUBLABEL_SHOW_HIDDEN_FILES, - "Pokaż ukryte pliki / katalogi w przeglądarce plików.") + "Pokaż ukryte pliki/katalogi w przeglądarce plików.") MSG_HASH(MENU_ENUM_SUBLABEL_SSH_ENABLE, "Włącz lub wyłącz zdalny dostęp do wiersza poleceń.") MSG_HASH(MENU_ENUM_SUBLABEL_SUSPEND_SCREENSAVER_ENABLE, @@ -1912,9 +1994,9 @@ MSG_HASH(MENU_ENUM_SUBLABEL_USER_LANGUAGE, MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_BLACK_FRAME_INSERTION, "Wstawia czarną klatke między klatkami. Przydatny dla użytkowników z ekranami 120Hz, którzy chcą odtwarzać zawartość 60 Hz, aby wyeliminować efekt duchów.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY, - "Zmniejsza opóźnienie kosztem większego ryzyka rwania wideo. Dodaje opóźnienie po V-Sync (w ms).") + "Zmniejsza opóźnienie kosztem większego ryzyka rwania obrazu. Dodaje opóźnienie po V-Sync (w ms).") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC_FRAMES, - "Określa liczbę klatek, jaką procesor może uruchomić przed GPU, gdy używana jest 'Hard GPU Sync'.") + "Określa liczbę klatek, jaką procesor może uruchomić przed GPU, gdy używana jest 'Trudna synchronizacja z GPU'.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MAX_SWAPCHAIN_IMAGES, "Informuje sterownik wideo, aby jawnie użył określonego trybu buforowania.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, @@ -1929,6 +2011,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_HELP_LIST, "Dowiedz się więcej o tym, jak działa program.") MSG_HASH(MSG_ADDED_TO_FAVORITES, "Dodano do ulubionych") +MSG_HASH(MSG_RESET_CORE_ASSOCIATION, + "Playlist entry core association has been reset.") MSG_HASH(MSG_APPENDED_DISK, "Dołączony dysk") MSG_HASH(MSG_APPLICATION_DIR, @@ -1942,7 +2026,7 @@ MSG_HASH(MSG_AUDIO_MUTED, MSG_HASH(MSG_AUDIO_UNMUTED, "Dźwięk nie jest wyciszony.") MSG_HASH(MSG_AUTOCONFIG_FILE_ERROR_SAVING, - "Błąd podczas zapisywania pliku autoconf.") + "Błąd podczas zapisywania pliku autoconfig.") MSG_HASH(MSG_AUTOCONFIG_FILE_SAVED_SUCCESSFULLY, "Plik Autoconfig został pomyślnie zapisany.") MSG_HASH(MSG_AUTOSAVE_FAILED, @@ -1958,7 +2042,7 @@ MSG_HASH(MSG_BYTES, MSG_HASH(MSG_CANNOT_INFER_NEW_CONFIG_PATH, "Nie można określić nowej ścieżki konfiguracji. Użyj bieżącego czasu.") MSG_HASH(MSG_CHEEVOS_HARDCORE_MODE_ENABLE, - "Tryb Hardcore włączony, savestate i rewind były wyłączone.") + "Tryb Hardcore włączony, stan zapisu i przewijanie do tyłu były wyłączone.") MSG_HASH(MSG_COMPARING_WITH_KNOWN_MAGIC_NUMBERS, "Porównując ze znanymi magicznymi liczbami...") MSG_HASH(MSG_COMPILED_AGAINST_API, @@ -2009,6 +2093,8 @@ MSG_HASH(MSG_DISK_EJECTED, "Wyrzucony") MSG_HASH(MSG_DOWNLOADING, "Ściąganie") +MSG_HASH(MSG_INDEX_FILE, + "Index") MSG_HASH(MSG_DOWNLOAD_FAILED, "Pobieranie nie udane") MSG_HASH(MSG_ERROR, @@ -2138,9 +2224,9 @@ MSG_HASH(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING, MSG_HASH(MSG_INFLATED_CHECKSUM_DID_NOT_MATCH_CRC32, "Skomplikowana suma kontrolna nie pasuje do CRC32.") MSG_HASH(MSG_INPUT_CHEAT, - "Wejdź w Cheat") + "Wejdź w kod") MSG_HASH(MSG_INPUT_CHEAT_FILENAME, - "Wprowadź nazwe Cheata") + "Wprowadź nazwe kodu") MSG_HASH(MSG_INPUT_PRESET_FILENAME, "Wprowadź wstępnie ustawioną nazwę pliku") MSG_HASH(MSG_INPUT_RENAME_ENTRY, @@ -2294,7 +2380,7 @@ MSG_HASH(MSG_TAKING_SCREENSHOT, MSG_HASH(MSG_TO, "do") MSG_HASH(MSG_UNDID_LOAD_STATE, - "Anulować stan obciążenia.") + "Anulować stan zpisu.") MSG_HASH(MSG_UNDOING_SAVE_STATE, "Cofanie stanu zapisu") MSG_HASH(MSG_UNKNOWN, @@ -2320,11 +2406,11 @@ MSG_HASH(MSG_VERSION_OF_LIBRETRO_API, MSG_HASH(MSG_VIEWPORT_SIZE_CALCULATION_FAILED, "Obliczenie wielkości wyświetlania nie powiodło się! Będzie nadal korzystać z nieprzetworzonych danych. Prawdopodobnie to nie zadziała ...") MSG_HASH(MSG_VIRTUAL_DISK_TRAY, - "wirtualna taca dysku.") + "Wirtualna taca dysku.") MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_LATENCY, "Pożądane opóźnienie dźwięku w milisekundach. Może nie być honorowane, jeśli sterownik audio nie może zapewnić określonego opóźnienia.") MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MUTE, - "Wycisz / włącz dźwięk.") + "Wycisz/włącz dźwięk.") MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_RATE_CONTROL_DELTA, "Pomaga wygładzać niedoskonałości w synchronizacji czasu podczas synchronizowania dźwięku i obrazu. Pamiętaj, że jeśli jest wyłączona, właściwa synchronizacja jest prawie niemożliwa do uzyskania." @@ -2475,9 +2561,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_RELEASEDATE_BY_MONTH, MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_RELEASEDATE_BY_YEAR, "Baza danych - Filtr: Data wydania na rok") MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_EDGE_MAGAZINE_ISSUE, - "Baza danych - Filter: Edge Magazine Issue") + "Baza danych - Filter: Problem z magazynem Edge") MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_EDGE_MAGAZINE_RATING, - "Baza danych - Filtr: Ocena czasopisma Edge") + "Baza danych - Filtr: Ocena magazynu Edge") MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_DATABASE_INFO, "Informacje o bazie danych") MSG_HASH(MSG_WIFI_SCAN_COMPLETE, @@ -2485,7 +2571,7 @@ MSG_HASH(MSG_WIFI_SCAN_COMPLETE, MSG_HASH(MSG_SCANNING_WIRELESS_NETWORKS, "Skanowanie sieci bezprzewodowych...") MSG_HASH(MSG_NETPLAY_LAN_SCAN_COMPLETE, - "Zakończono skanowanie Netplay.") + "Zakończono skanowanie gry online.") MSG_HASH(MSG_NETPLAY_LAN_SCANNING, "Skanowanie w poszukiwaniu hostów netplay...") MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, @@ -2553,7 +2639,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_USERNAME, MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_PASSWORD, "Wprowadź hasło do swojego konta RetroAchievements.") MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_NICKNAME, - "Wprowadź tutaj swoją nazwę użytkownika. Będzie to wykorzystywane między innymi do sesji netplay.") + "Wprowadź tutaj swoją nazwę użytkownika. Będzie to wykorzystywane między innymi do gry internetowej.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_POST_FILTER_RECORD, "Przechwyć obraz po zastosowaniu filtrów (ale nie shaderów). Twój film będzie wyglądał tak fantazyjnie jak to, co widzisz na ekranie.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_LIST, @@ -2583,7 +2669,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FONT_SIZE, MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_HIDE_IN_MENU, "Ukryj nakładkę w menu i pokaż ją ponownie po wyjściu z menu.") MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS, - "Pokaż wejścia klawiatury / kontrolera na nakładce ekranowej.") + "Pokaż wejścia klawiatury/kontrolera na nakładce ekranowej.") MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS_PORT, "Wybierz port dla nakładki, aby usłyszeć, czy opcja Pokaż nakładki na nakładkę jest włączona.") MSG_HASH( @@ -2592,7 +2678,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_SCALE_INTEGER, - "Skaluje wideo tylko w krokach całkowitych. Rozmiar bazy zależy od raportowanej przez system geometrii i współczynnika kształtu. Jeśli 'Force Aspect' nie jest ustawione, X / Y będzie liczbą całkowitą skalowaną niezależnie." + "Skaluje wideo tylko w krokach całkowitych. Rozmiar bazy zależy od raportowanej przez system geometrii i współczynnika kształtu. Jeśli 'Force Aspect' nie jest ustawione, X/Y będzie liczbą całkowitą skalowaną niezależnie." ) MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_GPU_SCREENSHOT, @@ -2620,7 +2706,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_INDEX, - "WPodczas zapisywania stanu, wskaźnik stanu zapisu jest automatycznie zwiększany przed zapisaniem. Podczas ładowania zawartości indeks zostanie ustawiony na najwyższy istniejący indeks." + "Podczas zapisywania stanu, wskaźnik stanu zapisu jest automatycznie zwiększany przed zapisaniem. Podczas ładowania zawartości indeks zostanie ustawiony na najwyższy istniejący indeks." ) MSG_HASH( MENU_ENUM_SUBLABEL_BLOCK_SRAM_OVERWRITE, @@ -2632,7 +2718,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO, - "W zwolnionym tempie zawartość spowalnia o określony / ustawiony współczynnik." + "W zwolnionym tempie zawartość spowalnia o określony/ustawiony współczynnik." ) MSG_HASH( MENU_ENUM_SUBLABEL_REWIND_ENABLE, @@ -2676,7 +2762,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_MENU_INPUT_SWAP_OK_CANCEL, - "Zamień przyciski na OK / Cancel. Wyłączone to orientacja japońskiego przycisku, włączona jest orientacja zachodnia." + "Zamień przyciski na OK/Anuluj. Wyłączone to orientacja japońskiego przycisku, włączona jest orientacja zachodnia." ) MSG_HASH( MENU_ENUM_SUBLABEL_PAUSE_LIBRETRO, @@ -2688,7 +2774,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_DRIVER, - "Sterownik audio do nase." + "Sterownik audio do użycia." ) MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_DRIVER, @@ -2824,7 +2910,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_TIMEDATE_ENABLE, - "Pokazuje aktualną datę i / lub czas w menu." + "Pokazuje aktualną datę i/lub czas w menu." ) MSG_HASH( MENU_ENUM_SUBLABEL_BATTERY_LEVEL_ENABLE, @@ -2832,7 +2918,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_NAVIGATION_WRAPAROUND, - "Oblewanie do początku i / lub końca, jeśli granica listy jest osiągnięta poziomo lub pionowo." + "Przewijanie do początku i/lub końca, jeśli granica listy jest osiągnięta poziomo lub pionowo." ) MSG_HASH( MENU_ENUM_SUBLABEL_NETPLAY_ENABLE_HOST, @@ -2855,6 +2941,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sortuj stany zachowywania w folderach nazwanych po używanym rdzeniu." ) +MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REQUEST_DEVICE_I, + "Request to play with the given input device.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "Adres URL do głównego katalogu Updater na kompilatorze Libretro.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, @@ -2903,9 +2991,9 @@ MSG_HASH(MENU_ENUM_SUBLABEL_LOAD_STATE, MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_STATE, "Zapisz stan w aktualnie wybranym gnieździe.") MSG_HASH(MENU_ENUM_SUBLABEL_RESUME, - "Wznów aktualnie uruchomioną zawartość i opuść Szybkie menu.") + "Wznów aktualnie uruchomioną zawartość i opuść szybkie menu.") MSG_HASH(MENU_ENUM_SUBLABEL_RESUME_CONTENT, - "Wznów aktualnie uruchomioną zawartość i opuść Szybkie men.") + "Wznów aktualnie uruchomioną zawartość i opuść szybkie menu.") MSG_HASH(MENU_ENUM_SUBLABEL_STATE_SLOT, "Zmienia aktualnie wybrany przedział stanu.") MSG_HASH(MENU_ENUM_SUBLABEL_UNDO_LOAD_STATE, @@ -2929,7 +3017,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE, MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_CURRENT_CONFIG_OVERRIDE_GAME, "Zapisuje plik konfiguracji zastąpienia, który będzie dotyczył tylko bieżącej treści. Ma pierwszeństwo przed główną konfiguracją.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_CHEAT_OPTIONS, - "Skonfiguruj kody oszustw.") + "Skonfiguruj kody.") MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_OPTIONS, "Skonfiguruj shadery, aby wizualnie powiększyć obraz.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_INPUT_REMAPPING_OPTIONS, @@ -2948,12 +3036,12 @@ MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_BROWSER_DIRECTORY, "Ustawia katalog startowy dla przeglądarki plików.") MSG_HASH( MENU_ENUM_SUBLABEL_CONTENT_DIR, - "Zwykle ustawiane przez programistów, którzy pakują aplikacje libretro / RetroArch, aby wskazywały na zasoby." + "Zwykle ustawiane przez programistów, którzy pakują aplikacje libretro/RetroArch, aby wskazywały na zasoby." ) MSG_HASH(MENU_ENUM_SUBLABEL_DYNAMIC_WALLPAPERS_DIRECTORY, "Katalog do przechowywania tapet dynamicznie ładowanych przez menu w zależności od kontekstu.") MSG_HASH(MENU_ENUM_SUBLABEL_THUMBNAILS_DIRECTORY, - "Dodatkowe miniaturki (boxarty / miscale itp.) Są tutaj przechowywane." + "Dodatkowe miniaturki (boxarty/miscale itp.) Są tutaj przechowywane." ) MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_CONFIG_DIRECTORY, "Ustawia katalog początkowy dla przeglądarki konfiguracji menu.") @@ -3016,9 +3104,9 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CORE_ASSETS_DIRECTORY, MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_REMAPPING_DIRECTORY, "Zapisz wszystkie zmienione kontrolki do tego katalogu.") MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_DIR_PATH, - "Katalog, w którym program szuka treści / rdzeni.") + "Katalog, w którym program szuka treści/rdzeni.") MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_INFO_PATH, - "Pliki informacji o aplikacji / rdzeniu są tutaj przechowywane.") + "Pliki informacji o aplikacji/rdzeniu są tutaj przechowywane.") MSG_HASH(MENU_ENUM_SUBLABEL_JOYPAD_AUTOCONFIG_DIR, "Jeśli joypad jest podłączony, to joypad zostanie automatycznie skonfigurowany, jeśli plik konfiguracyjny odpowiadający mu jest obecny w tym katalogu.") MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_DIRECTORY, @@ -3076,7 +3164,9 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET, MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_AS, "Zapisz bieżące ustawienia modułu cieniującego jako nowe ustawienie domyślne modułu cieniującego.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_CORE, - "Zapisz bieżące ustawienia modułu cieniującego jako ustawienia domyślne dla tej aplikacji / rdzenia.") + "Zapisz bieżące ustawienia modułu cieniującego jako ustawienia domyślne dla tej aplikacji/rdzenia.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_PARENT, + "Save the current shader settings as the default settings for all files in the current content directory.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_GAME, "Zapisz bieżące ustawienia modułu cieniującego jako ustawienia domyślne dla zawartości.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PARAMETERS, @@ -3085,10 +3175,10 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_PARAMETERS, "Modyfikuje sam domyślne ustawienie modułu cieniującego w menu.") MSG_HASH( MENU_ENUM_SUBLABEL_CHEAT_NUM_PASSES, - "Zwiększ lub zmniejsz ilość cheatów." + "Zwiększ lub zmniejsz ilość kodów." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_APPLY_CHANGES, - "Zmiany Cheat odniosą skutek natychmiast.") + "Zmiany kodu odniosą skutek natychmiast.") MSG_HASH( MENU_ENUM_SUBLABEL_CHEAT_FILE_LOAD, "Załaduj plik oszukiwać." @@ -3100,25 +3190,29 @@ MSG_HASH( MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SETTINGS, "Szybki dostęp do wszystkich istotnych ustawień w grze.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_INFORMATION, - "Wyświetl informacje dotyczące aplikacji / rdzenia.") + "Wyświetl informacje dotyczące aplikacji/rdzenia.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_ASPECT_RATIO, - "Wartość zmiennoprzecinkowa dla współczynnika proporcji wideo (szerokość / wysokość), jeśli współczynnik proporcji jest ustawiony na 'Config'.") + "Wartość zmiennoprzecinkowa dla współczynnika proporcji wideo (szerokość/wysokość), jeśli współczynnik proporcji jest ustawiony na 'Config'.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_HEIGHT, - "Niestandardowa wysokość rzutni, która jest używana, jeśli współczynnik proporcji jest ustawiony na 'Niestandardowy'.") + "Niestandardowa wysokość ekranu, która jest używana, jeśli współczynnik proporcji jest ustawiony na 'Niestandardowy'.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_WIDTH, - "Niestandardowa szerokość rzutni, która jest używana, jeśli współczynnik proporcji jest ustawiony na 'Niestandardowy'.") + "Niestandardowa szerokość ekranu, która jest używana, jeśli współczynnik proporcji jest ustawiony na 'Niestandardowy'.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_X, - "Niestandardowe przesunięcie wyświetlania używane do definiowania położenia osi X w rzutni. Są one ignorowane, jeśli włączona jest opcja 'Integer Scale'. Zostanie wtedy automatycznie wyśrodkowany.") + "Niestandardowe przesunięcie wyświetlania używane do definiowania położenia osi X. Są one ignorowane, jeśli włączona jest opcja 'Integer Scale'. Zostanie wtedy automatycznie wyśrodkowany.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_Y, - "Niestandardowe przesunięcie wyświetlania używane do definiowania położenia osi Y w rzutni. Są one ignorowane, jeśli włączona jest opcja 'Integer Scale'. Zostanie wtedy automatycznie wyśrodkowany.") + "Niestandardowe przesunięcie wyświetlania używane do definiowania położenia osi Y. Są one ignorowane, jeśli włączona jest opcja 'Integer Scale'. Zostanie wtedy automatycznie wyśrodkowany.") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_USE_MITM_SERVER, "Użyj serwera przekazywania") MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_USE_MITM_SERVER, - "Przekaż połączenia sieciowe przez serwer pośredniczący. Przydatne, jeśli host znajduje się za zaporą lub ma problemy z NAT / UPnP.") + "Przekaż połączenia sieciowe przez serwer pośredniczący. Przydatne, jeśli host znajduje sięs za zaporą lub ma problemy z NAT/UPnP.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_MITM_SERVER, + "Lokalizacja serwera przekaźnikowego") +MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_MITM_SERVER, + "Wybierz określony serwer przekazujący, którego chcesz użyć. Geograficznie bliższe lokalizacje mają zazwyczaj mniejsze opóźnienie.") MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_MIXER, - "Dodaj do mikser") + "Dodaj do miksera") MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_MIXER_AND_COLLECTION, - "Dodaj do mikser") + "Dodaj do miksera") MSG_HASH(MENU_ENUM_LABEL_VALUE_FILTER_BY_CURRENT_CORE, "Filtruj według bieżącego rdzenia") MSG_HASH( @@ -3135,14 +3229,14 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_MUTE, - "Mikser dźwięku Wycisz" + "Wycisz mikser dźwięku" ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MIXER_MUTE, - "Wycisz / włącz dźwięk miksera.") + "Wycisz/włącz dźwięk miksera.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_ONLINE_UPDATER, - "Pokaż Online Updater") + "Pokaż aktualizacje online") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_ONLINE_UPDATER, - "Pokaż / ukryj opcję 'Online Update'.") + "Pokaż/ukryj opcję 'Aktualizacja online'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_VIEWS_SETTINGS, "Widok") MSG_HASH( @@ -3152,7 +3246,7 @@ MSG_HASH( MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_CORE_UPDATER, "Pokaż program Updater rdzenia") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_CORE_UPDATER, - "Pokaż / ukryj możliwość aktualizacji rdzeni (i podstawowych plików informacyjnych).") + "Pokaż/ukryj możliwość aktualizacji rdzeni (i podstawowych plików informacyjnych).") MSG_HASH(MSG_PREPARING_FOR_CONTENT_SCAN, "Przygotowanie do skanowania zawartości...") MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_DELETE, @@ -3160,7 +3254,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_DELETE, MSG_HASH(MENU_ENUM_SUBLABEL_CORE_DELETE, "Usuń ten rdzeń z dysku.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_FRAMEBUFFER_OPACITY, - "Framebuffer Opacity") + "Nieprzezroczysty buffor klatki") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_FRAMEBUFFER_OPACITY, "Zmodyfikuj krycie bufora klatki.") MSG_HASH(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES, @@ -3182,7 +3276,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_GOTO_VIDEO, MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_ICONS_ENABLE, "Ikony menu") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_ICONS_ENABLE, - "Włącz / wyłącz ikony menu pokazane po lewej stronie wpisów menu.") + "Włącz/wyłącz ikony menu pokazane po lewej stronie wpisów menu.") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MAIN_MENU_ENABLE_SETTINGS, "Włącz kartę Ustawienia") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS_PASSWORD, @@ -3208,31 +3302,31 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RENAME_ENTRY, MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_LOAD_CORE, "Pokaż ładowanie rdzenia") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_LOAD_CORE, - "Pokaż / ukryj opcję 'Załaduj rdzeń'.") + "Pokaż/ukryj opcję 'Załaduj rdzeń'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_LOAD_CONTENT, "Pokaż ładunek zawartości") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_LOAD_CONTENT, - "Pokaż / ukryj opcję \"Wczytaj zawartość\".") + "Pokaż/ukryj opcję \"Wczytaj zawartość\".") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_INFORMATION, "Pokaż informacje") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_INFORMATION, - "Show/hide the 'Information' option.") + "Pokaż/ukryj opcję 'Informacje'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_CONFIGURATIONS, "Pokaż konfiguracje") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_CONFIGURATIONS, - "Pokaż / ukryj opcję 'Konfiguracje'.") + "Pokaż/ukryj opcję 'Konfiguracje'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_HELP, "Pokaż pomoc") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_HELP, - "Pokaż / ukryj opcję \"Pomoc\".") + "Pokaż/ukryj opcję \"Pomoc\".") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_QUIT_RETROARCH, "Pokaż Zamknij RetroArch") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_QUIT_RETROARCH, - "Pokaż / ukryj opcję 'Zamknij RetroArch'.") + "Pokaż/ukryj opcję 'Zamknij RetroArch'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_REBOOT, "Pokaż restart") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_REBOOT, - "Pokaż / ukryj opcję 'Reboot'.") + "Pokaż/ukryj opcję 'Restart'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_VIEWS_SETTINGS, "Szybkie menu") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_VIEWS_SETTINGS, @@ -3240,47 +3334,47 @@ MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_VIEWS_SETTINGS, MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_TAKE_SCREENSHOT, "Pokaż zrzut ekranu") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_TAKE_SCREENSHOT, - "Pokaż / ukryj opcję \"Zrób zrzut ekranu\".") + "Pokaż/ukryj opcję \"Zrób zrzut ekranu\".") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SAVE_LOAD_STATE, - "Pokaż stan zapisywania / ładowania") + "Pokaż stan zapisywania/ładowania") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_LOAD_STATE, - "Pokaż / ukryj opcje zapisywania / ładowania stanu.") + "Pokaż/ukryj opcje zapisywania/ładowania stanu.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_UNDO_SAVE_LOAD_STATE, - "Pokaż Cofnij zapisanie / załadowanie stanu") + "Pokaż cofnij zapisanie/załadowanie stanu") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_UNDO_SAVE_LOAD_STATE, - "Pokaż / ukryj opcje cofania stanu zapisywania / ładowania.") + "Pokaż/ukryj opcje cofania stanu zapisywania/ładowania.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_ADD_TO_FAVORITES, - "Pokaż Dodaj do ulubionych") + "Pokaż dodaj do ulubionych") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_ADD_TO_FAVORITES, - "Pokaż / ukryj opcję 'Dodaj do ulubionych'.") + "Pokaż/ukryj opcję 'Dodaj do ulubionych'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_OPTIONS, "Pokaż opcje") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_OPTIONS, - "Pokaż / ukryj opcję 'Opcje'.") + "Pokaż/ukryj opcję 'Opcje'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_CONTROLS, "Pokaż elementy sterujące") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_CONTROLS, - "Pokaż / ukryj opcję 'Sterowanie'.") + "Pokaż/ukryj opcję 'Sterowanie'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_CHEATS, "Pokaż kody") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_CHEATS, - "Pokaż / ukryj opcję 'Cheaty'.") + "Pokaż/ukryj opcję 'Kody'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SHADERS, "Pokaż shadery") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SHADERS, - "Pokaż / ukryj opcję 'Shader'.") + "Pokaż/ukryj opcję 'Shadery'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SAVE_CORE_OVERRIDES, "Pokaż zapis nadpisu rdzenia") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_CORE_OVERRIDES, - "Pokaż / ukryj opcję 'Zapisz przesłonięcia podstawowe'.") + "Pokaż/ukryj opcję 'Zapisz podstawowe przesłonięcia'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SAVE_GAME_OVERRIDES, - "Pokaż zapisywanie zmian gry") + "Pokaż zapisy gry") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_GAME_OVERRIDES, - "Pokaż / ukryj opcję 'Zachowaj pominięcia gry'.") + "Pokaż/ukryj opcję 'Zachowaj pominięcia gry'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_INFORMATION, "Pokaż informacje") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_INFORMATION, - "Pokaż / ukryj opcję 'Informacje'.") + "Pokaż/ukryj opcję 'Informacje'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_ENABLE, "Tło powiadomień Włącz") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_RED, @@ -3332,10 +3426,16 @@ MSG_HASH(MSG_SCANNING_OF_FILE_FINISHED, MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_OPACITY, "Przezroczystość okna") MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_QUALITY, - "Audio Resampler Quality") + "Jakość resamplera audio") MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_QUALITY, - "Lower this value to favor performance/lower latency over audio quality, increase if you want better audio quality at the expense of performance/lower latency.") + "Obniż tę wartość, aby faworyzować wydajność/zmniejszyć opóźnienie w stosunku do jakości dźwięku, zwiększaj, jeśli chcesz uzyskać lepszą jakość dźwięku kosztem wydajności/mniejszego opóźnienia.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_WATCH_FOR_CHANGES, + "Oglądaj pliki modułu cieniującego w poszukiwaniu zmian") +MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_WATCH_FOR_CHANGES, + "Automatycznie zastosuj zmiany wprowadzone w plikach modułu cieniującego na dysku.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_SHOW_DECORATIONS, + "Pokaż dekoracje okien") MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, - "Display Statistics") + "Wyświetl statystyki") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, - "Show onscreen technical statistics.") + "Pokaż techniczne statystyki na ekranie.") From 86d24a0fc428dfad8353f0a30f88cbf7d25cc72b Mon Sep 17 00:00:00 2001 From: Dwedit Date: Wed, 28 Mar 2018 10:08:57 -0500 Subject: [PATCH 016/517] Fix invalid long command line options causing infinite loop on Windows (issue #6477) --- retroarch.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/retroarch.c b/retroarch.c index 3148d556bf..ed8ef60679 100644 --- a/retroarch.c +++ b/retroarch.c @@ -743,6 +743,11 @@ static void retroarch_parse_input_and_config(int argc, char *argv[]) RARCH_OVERRIDE_SETTING_STATE_PATH, NULL); break; + /* Must handle '?' otherwise you get an infinite loop */ + case '?': + retroarch_print_help(argv[0]); + retroarch_fail(1, "retroarch_parse_input()"); + break; /* All other arguments are handled in the second pass */ } } From 2c1cb6cf55641b03a70f2a613a157b9db4acdd35 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Wed, 28 Mar 2018 10:33:37 -0500 Subject: [PATCH 017/517] Rename "cached_device" to "cached_device_d3d11" avoid a name conflict on griffin builds --- gfx/drivers/d3d11.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 8ee67d0c10..cddf096d83 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -39,7 +39,7 @@ #include "../drivers_shader/slang_process.h" #endif -static D3D11Device cached_device; +static D3D11Device cached_device_d3d11; static D3D_FEATURE_LEVEL cached_supportedFeatureLevel; static D3D11DeviceContext cached_context; @@ -555,7 +555,7 @@ static void d3d11_gfx_free(void* data) if (video_driver_is_video_cache_context()) { - cached_device = d3d11->device; + cached_device_d3d11 = d3d11->device; cached_context = d3d11->context; cached_supportedFeatureLevel = d3d11->supportedFeatureLevel; } @@ -632,13 +632,13 @@ d3d11_gfx_init(const video_info_t* video, const input_driver_t** input, void** i #ifdef DEBUG flags |= D3D11_CREATE_DEVICE_DEBUG; #endif - if(cached_device && cached_context) + if(cached_device_d3d11 && cached_context) { IDXGIFactory* dxgiFactory = NULL; IDXGIDevice* dxgiDevice = NULL; IDXGIAdapter* adapter = NULL; - d3d11->device = cached_device; + d3d11->device = cached_device_d3d11; d3d11->context = cached_context; d3d11->supportedFeatureLevel = cached_supportedFeatureLevel; From 163be3ccfd1d2e1a20f741846684c15c7b0bd0a2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 28 Mar 2018 17:41:04 +0200 Subject: [PATCH 018/517] (vulkan_common.c) Prevent some more collissions in the future --- gfx/common/vulkan_common.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 3f789fa8e7..5ea05e04da 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -31,10 +31,10 @@ #include "../../libretro-common/include/retro_timers.h" #include "../../configuration.h" -static dylib_t vulkan_library; -static VkInstance cached_instance; -static VkDevice cached_device; -static retro_vulkan_destroy_device_t cached_destroy_device; +static dylib_t vulkan_library; +static VkInstance cached_instance_vk; +static VkDevice cached_device_vk; +static retro_vulkan_destroy_device_t cached_destroy_device_vk; #ifdef VULKAN_DEBUG static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_cb( @@ -1437,7 +1437,7 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) iface = NULL; } - if (!cached_device && iface && iface->create_device) + if (!cached_device_vk && iface && iface->create_device) { struct retro_vulkan_context context = { 0 }; const VkPhysicalDeviceFeatures features = { 0 }; @@ -1478,10 +1478,10 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) } } - if (cached_device && cached_destroy_device) + if (cached_device_vk && cached_destroy_device_vk) { - vk->context.destroy_device = cached_destroy_device; - cached_destroy_device = NULL; + vk->context.destroy_device = cached_destroy_device_vk; + cached_destroy_device_vk = NULL; } if (!vulkan_context_init_gpu(vk)) @@ -1565,10 +1565,10 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) device_info.ppEnabledLayerNames = device_layers; #endif - if (cached_device) + if (cached_device_vk) { - vk->context.device = cached_device; - cached_device = NULL; + vk->context.device = cached_device_vk; + cached_device_vk = NULL; video_driver_set_video_cache_context_ack(); RARCH_LOG("[Vulkan]: Using cached Vulkan context.\n"); @@ -1736,10 +1736,10 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, } } - if (cached_instance) + if (cached_instance_vk) { - vk->context.instance = cached_instance; - cached_instance = NULL; + vk->context.instance = cached_instance_vk; + cached_instance_vk = NULL; res = VK_SUCCESS; } else @@ -2239,9 +2239,9 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk, if (video_driver_is_video_cache_context()) { - cached_device = vk->context.device; - cached_instance = vk->context.instance; - cached_destroy_device = vk->context.destroy_device; + cached_device_vk = vk->context.device; + cached_instance_vk = vk->context.instance; + cached_destroy_device_vk = vk->context.destroy_device; } else { @@ -2253,6 +2253,7 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk, vk->context.destroy_device(); vkDestroyInstance(vk->context.instance, NULL); + if (vulkan_library) { dylib_close(vulkan_library); From 994ca6b3f8d239feb37f286e3c8d1ab2fe1f787c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 28 Mar 2018 18:07:31 +0200 Subject: [PATCH 019/517] Enable HAVE_CHEEVOS for MSVC 2003 target too --- pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj | 16 ++++++++-------- pkg/msvc/msvc-2005/RetroArch-msvc2005.vcproj | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj b/pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj index c09ab98844..443c10edce 100644 --- a/pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj +++ b/pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj @@ -21,14 +21,14 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\gfx\include"" - PreprocessorDefinitions="_WIN32;WINVER=0x0400;_WIN32_WINNT=0x0400;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_GRIFFIN;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;WANT_ZLIB;HAVE_DINPUT;HAVE_DSOUND;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_OVERLAY;HAVE_RGUI;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT;HAVE_D3D;HAVE_D3D8;DEBUG;_DEBUG;__STDC_CONSTANT_MACROS" + PreprocessorDefinitions="_WIN32;WINVER=0x0400;_WIN32_WINNT=0x0400;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;WANT_ZLIB;HAVE_DINPUT;HAVE_DSOUND;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_OVERLAY;HAVE_RGUI;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT;HAVE_D3D;HAVE_D3D8;DEBUG;_DEBUG;__STDC_CONSTANT_MACROS" MinimalRebuild="TRUE" BasicRuntimeChecks="3" RuntimeLibrary="5" UsePrecompiledHeader="0" WarningLevel="3" Detect64BitPortabilityProblems="TRUE" - DebugInformationFormat="4"/> + DebugInformationFormat="1"/> + DebugInformationFormat="1"/> + DebugInformationFormat="1"/> + DebugInformationFormat="1"/> Date: Wed, 28 Mar 2018 18:33:31 +0200 Subject: [PATCH 020/517] (MSVC 2013/2015/2017) Add HAVE_CHEEVOS support --- pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj | 10 +++++----- pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj | 16 ++++++++-------- pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj | 16 ++++++++-------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj b/pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj index 7e3efe3a57..2eca6af1a1 100644 --- a/pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj +++ b/pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj @@ -84,7 +84,7 @@ Level3 Disabled - WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;_DEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) + WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;_DEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) $(SolutionDir)\..\..\gfx\include\dxsdk;$(SolutionDir)\..\..\gfx\include;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\glslang;$(SolutionDir)\..\..\deps\SPIRV-Cross;%(AdditionalIncludeDirectories) @@ -99,7 +99,7 @@ Level3 Disabled - WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_UPDATE_ASSETS;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;_DEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) + WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_UPDATE_ASSETS;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;_DEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) $(SolutionDir)\..\..\gfx\include\dxsdk;$(SolutionDir)\..\..\gfx\include;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\glslang;$(SolutionDir)\..\..\deps\SPIRV-Cross;%(AdditionalIncludeDirectories) @@ -116,7 +116,7 @@ MaxSpeed true true - WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;HAVE_MENU;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;NDEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_COMMAND;HAVE_NETWORK_CMD;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) + WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;HAVE_MENU;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;NDEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_COMMAND;HAVE_NETWORK_CMD;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) $(SolutionDir)\..\..\gfx\include\dxsdk;$(SolutionDir)\..\..\gfx\include;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\glslang;$(SolutionDir)\..\..\deps\SPIRV-Cross;%(AdditionalIncludeDirectories) @@ -135,7 +135,7 @@ MaxSpeed true true - WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_UPDATE_ASSETS;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;NDEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) + WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_UPDATE_ASSETS;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;NDEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) $(SolutionDir)\..\..\gfx\include\dxsdk;$(SolutionDir)\..\..\gfx\include;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\glslang;$(SolutionDir)\..\..\deps\SPIRV-Cross;%(AdditionalIncludeDirectories) @@ -159,4 +159,4 @@ - \ No newline at end of file + diff --git a/pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj b/pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj index eb0955f903..f9f51b52a9 100644 --- a/pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj +++ b/pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj @@ -190,7 +190,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -209,7 +209,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -229,7 +229,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -249,7 +249,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -271,7 +271,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -295,7 +295,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -320,7 +320,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -344,7 +344,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp diff --git a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj index 14aee7a1c8..824877fbb4 100644 --- a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj +++ b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj @@ -191,7 +191,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -210,7 +210,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_FBO;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_FBO;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -230,7 +230,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -249,7 +249,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_FBO;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_FBO;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -271,7 +271,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -295,7 +295,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -320,7 +320,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -344,7 +344,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp From f5e0346fc24aa193429d018a8bf10927b4ab8114 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Wed, 28 Mar 2018 14:22:07 -0500 Subject: [PATCH 021/517] Runahead system --- config.def.h | 6 + configuration.c | 4 + configuration.h | 4 + core.h | 3 + core_impl.c | 14 + dynamic.c | 35 +- dynamic.h | 3 + griffin/griffin.c | 10 + intl/msg_hash_lbl.h | 6 + intl/msg_hash_us.h | 18 + libretro-common/compat/unlink_utf8.c | 30 ++ libretro-common/include/compat/unlink_utf8.h | 23 + menu/cbs/menu_cbs_sublabel.c | 12 + menu/menu_displaylist.c | 9 + menu/menu_setting.c | 45 ++ msg_hash.h | 3 + retroarch.c | 12 +- runahead/copy_load_info.c | 177 +++++++ runahead/copy_load_info.h | 16 + runahead/dirty_input.c | 166 +++++++ runahead/dirty_input.h | 15 + runahead/mem_util.c | 95 ++++ runahead/mem_util.h | 26 + runahead/mylist.c | 151 ++++++ runahead/mylist.h | 32 ++ runahead/run_ahead.c | 425 ++++++++++++++++ runahead/secondary_core.c | 487 +++++++++++++++++++ runahead/secondary_core.h | 19 + 28 files changed, 1841 insertions(+), 5 deletions(-) create mode 100644 libretro-common/compat/unlink_utf8.c create mode 100644 libretro-common/include/compat/unlink_utf8.h create mode 100644 runahead/copy_load_info.c create mode 100644 runahead/copy_load_info.h create mode 100644 runahead/dirty_input.c create mode 100644 runahead/dirty_input.h create mode 100644 runahead/mem_util.c create mode 100644 runahead/mem_util.h create mode 100644 runahead/mylist.c create mode 100644 runahead/mylist.h create mode 100644 runahead/run_ahead.c create mode 100644 runahead/secondary_core.c create mode 100644 runahead/secondary_core.h diff --git a/config.def.h b/config.def.h index 829ea18345..98430898f1 100644 --- a/config.def.h +++ b/config.def.h @@ -589,6 +589,12 @@ static const float slowmotion_ratio = 3.0; /* Maximum fast forward ratio. */ static const float fastforward_ratio = 0.0; +/* Run core logic one or more frames ahead then load the state back to reduce perceived input lag. */ +static const unsigned run_ahead_frames = 1; + +/* When using the Run Ahead feature, use a secondary instance of the core. */ +static const bool run_ahead_secondary_instance = true; + /* Enable stdin/network command interface. */ static const bool network_cmd_enable = false; static const uint16_t network_cmd_port = 55355; diff --git a/configuration.c b/configuration.c index 020302be65..2e259ebb12 100644 --- a/configuration.c +++ b/configuration.c @@ -1220,6 +1220,8 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("ui_menubar_enable", &settings->bools.ui_menubar_enable, true, true, false); SETTING_BOOL("suspend_screensaver_enable", &settings->bools.ui_suspend_screensaver_enable, true, true, false); SETTING_BOOL("rewind_enable", &settings->bools.rewind_enable, true, rewind_enable, false); + SETTING_BOOL("run_ahead_enabled", &settings->bools.run_ahead_enabled, true, false, false); + SETTING_BOOL("run_ahead_secondary_instance", &settings->bools.run_ahead_secondary_instance, true, false, false); SETTING_BOOL("audio_sync", &settings->bools.audio_sync, true, audio_sync, false); SETTING_BOOL("video_shader_enable", &settings->bools.video_shader_enable, true, shader_enable, false); SETTING_BOOL("video_shader_watch_files", &settings->bools.video_shader_watch_files, true, video_shader_watch_files, false); @@ -1513,6 +1515,8 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("video_msg_bgcolor_green", &settings->uints.video_msg_bgcolor_green, true, message_bgcolor_green, false); SETTING_UINT("video_msg_bgcolor_blue", &settings->uints.video_msg_bgcolor_blue, true, message_bgcolor_blue, false); + SETTING_UINT("run_ahead_frames", &settings->uints.run_ahead_frames, true, 1, false); + *size = count; return tmp; diff --git a/configuration.h b/configuration.h index 2f3e01eeb1..02b646cb0a 100644 --- a/configuration.h +++ b/configuration.h @@ -223,6 +223,8 @@ typedef struct settings bool playlist_entry_remove; bool playlist_entry_rename; bool rewind_enable; + bool run_ahead_enabled; + bool run_ahead_secondary_instance; bool pause_nonactive; bool block_sram_overwrite; bool savestate_auto_index; @@ -380,6 +382,8 @@ typedef struct settings unsigned input_remap_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END]; unsigned led_map[MAX_LEDS]; + + unsigned run_ahead_frames; } uints; struct diff --git a/core.h b/core.h index 35878206b4..06cdbac698 100644 --- a/core.h +++ b/core.h @@ -170,6 +170,9 @@ bool core_set_poll_type(unsigned *type); /* Runs the core for one frame. */ bool core_run(void); +/* Runs the core for one frame, but does not trigger any input polling */ +bool core_run_no_input_polling(void); + bool core_init(void); bool core_deinit(void *data); diff --git a/core_impl.c b/core_impl.c index 62285e6682..245f1bc31f 100644 --- a/core_impl.c +++ b/core_impl.c @@ -44,6 +44,9 @@ #include "gfx/video_driver.h" #include "audio/audio_driver.h" +#include "runahead/copy_load_info.h" +#include "runahead/secondary_core.h" + struct retro_callbacks retro_ctx; struct retro_core_t current_core; @@ -262,6 +265,9 @@ bool core_set_controller_port_device(retro_ctx_controller_info_t *pad) { if (!pad) return false; + + remember_controller_port_device(pad->port, pad->device); + current_core.retro_set_controller_port_device(pad->port, pad->device); return true; } @@ -277,6 +283,8 @@ bool core_get_memory(retro_ctx_memory_info_t *info) bool core_load_game(retro_ctx_load_content_info_t *load_info) { + set_load_content_info(load_info); + bool contentless = false; bool is_inited = false; @@ -424,6 +432,12 @@ bool core_run(void) return true; } +bool core_run_no_input_polling(void) +{ + current_core.retro_run(); + return true; +} + bool core_load(unsigned poll_type_behavior) { current_core.poll_type = poll_type_behavior; diff --git a/dynamic.c b/dynamic.c index 3e89144f81..86c7996cb4 100644 --- a/dynamic.c +++ b/dynamic.c @@ -66,9 +66,11 @@ #include "msg_hash.h" #include "verbosity.h" +#include "runahead/secondary_core.h" + #ifdef HAVE_DYNAMIC #define SYMBOL(x) do { \ - function_t func = dylib_proc(lib_handle, #x); \ + function_t func = dylib_proc(lib_handle_local, #x); \ memcpy(¤t_core->x, &func, sizeof(func)); \ if (current_core->x == NULL) { RARCH_ERR("Failed to load symbol: \"%s\"\n", #x); retroarch_fail(1, "init_libretro_sym()"); } \ } while (0) @@ -383,14 +385,32 @@ bool libretro_get_system_info(const char *path, * Setup libretro callback symbols. Returns true on success, * or false if symbols could not be loaded. **/ -static bool load_symbols(enum rarch_core_type type, struct retro_core_t *current_core) +bool init_libretro_sym_custom(enum rarch_core_type type, struct retro_core_t *current_core, const char *lib_path, dylib_t *lib_handle_p) { + /* the library handle for use with the SYMBOL macro */ + dylib_t lib_handle_local; switch (type) { case CORE_TYPE_PLAIN: #ifdef HAVE_DYNAMIC - if (!load_dynamic_core()) - return false; + + if (lib_path == NULL || lib_handle_p == NULL) + { + if (!load_dynamic_core()) + return false; + lib_handle_local = lib_handle; + } + else + { + /* for a secondary core, we already have a primary library loaded, so we can skip some checks and just load the library */ + retro_assert(lib_path != NULL && lib_handle_p != NULL); + lib_handle_local = dylib_load(lib_path); + if (lib_handle_local == NULL) + { + return false; + } + *lib_handle_p = lib_handle_local; + } #endif SYMBOL(retro_init); @@ -615,6 +635,11 @@ static bool load_symbols(enum rarch_core_type type, struct retro_core_t *current return true; } +static bool load_symbols(enum rarch_core_type type, struct retro_core_t *current_core) +{ + return init_libretro_sym_custom(type, current_core, NULL, NULL); +} + /** * init_libretro_sym: * @type : Type of core to be loaded. @@ -634,6 +659,8 @@ bool init_libretro_sym(enum rarch_core_type type, struct retro_core_t *current_c if (!load_symbols(type, current_core)) return false; + /* remember last core type created, so creating a secondary core will know what core type to use */ + set_last_core_type(type); return true; } diff --git a/dynamic.h b/dynamic.h index 2432704e48..e930a2edc8 100644 --- a/dynamic.h +++ b/dynamic.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "core_type.h" @@ -132,6 +133,8 @@ bool libretro_get_shared_context(void); bool init_libretro_sym(enum rarch_core_type type, struct retro_core_t *core); +bool init_libretro_sym_custom(enum rarch_core_type type, struct retro_core_t *current_core, const char *lib_path, dylib_t *lib_handle_p); + /** * uninit_libretro_sym: * diff --git a/griffin/griffin.c b/griffin/griffin.c index 7b321d959e..40b0f89ac7 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -56,6 +56,9 @@ COMPATIBILITY #include "../libretro-common/compat/compat_fnmatch.c" #include "../libretro-common/compat/fopen_utf8.c" +#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC +#include "../libretro-common/compat/unlink_utf8.c" +#endif #include "../libretro-common/memmap/memalign.c" /*============================================================ @@ -1252,6 +1255,13 @@ MENU #include "../libretro-common/net/net_http_parse.c" #endif +#include "../runahead/mem_util.c" +#include "../runahead/secondary_core.c" +#include "../runahead/run_ahead.c" +#include "../runahead/copy_load_info.c" +#include "../runahead/dirty_input.c" +#include "../runahead/mylist.c" + /*============================================================ DEPENDENCIES ============================================================ */ diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index e914210d27..4152488f14 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -989,6 +989,12 @@ MSG_HASH(MENU_ENUM_LABEL_SHUTDOWN, "shutdown") MSG_HASH(MENU_ENUM_LABEL_SLOWMOTION_RATIO, "slowmotion_ratio") +MSG_HASH(MENU_ENUM_LABEL_RUN_AHEAD_ENABLED, + "run_ahead_enabled") +MSG_HASH(MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, + "run_ahead_secondary_instance") +MSG_HASH(MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, + "run_ahead_frames") MSG_HASH(MENU_ENUM_LABEL_SORT_SAVEFILES_ENABLE, "sort_savefiles_enable") MSG_HASH(MENU_ENUM_LABEL_SORT_SAVESTATES_ENABLE, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index caca939fe1..97584df271 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1445,6 +1445,12 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SHUTDOWN, "Shutdown") MSG_HASH(MENU_ENUM_LABEL_VALUE_SLOWMOTION_RATIO, "Slow-Motion Ratio") +MSG_HASH(MENU_ENUM_LABEL_VALUE_RUN_AHEAD_ENABLED, + "Run-Ahead to Reduce Latency") +MSG_HASH(MENU_ENUM_LABEL_VALUE_RUN_AHEAD_FRAMES, + "Number of Frames to Run Ahead") +MSG_HASH(MENU_ENUM_LABEL_VALUE_RUN_AHEAD_SECONDARY_INSTANCE, + "Runahead Use Second Instance") MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVEFILES_ENABLE, "Sort Saves In Folders") MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVESTATES_ENABLE, @@ -2720,6 +2726,18 @@ MSG_HASH( MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO, "When in slow motion, content will slow down by the factor specified/set." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_RUN_AHEAD_ENABLED, + "Run core logic one or more frames ahead then load the state back to reduce perceived input lag." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_RUN_AHEAD_FRAMES, + "The number of frames to run ahead. Causes gameplay issues such as jitter if you exceed the number of lag frames internal to the game." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_RUN_AHEAD_SECONDARY_INSTANCE, + "Use a second instance of the RetroArch core to run ahead. Prevents audio problems due to loading state." + ) MSG_HASH( MENU_ENUM_SUBLABEL_REWIND_ENABLE, "Enable rewinding. This will take a performance hit when playing." diff --git a/libretro-common/compat/unlink_utf8.c b/libretro-common/compat/unlink_utf8.c new file mode 100644 index 0000000000..ce3bcad01e --- /dev/null +++ b/libretro-common/compat/unlink_utf8.c @@ -0,0 +1,30 @@ +#include +#include +#include "boolean.h" +#include + +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) +#ifndef LEGACY_WIN32 +#define LEGACY_WIN32 +#endif +#endif + +#ifdef _WIN32 + +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include + +bool unlink_utf8(const char * filename) +{ +#if defined(LEGACY_WIN32) + bool result = DeleteFileA(filename_w); +#else + wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); + bool result = DeleteFileW(filename_w); + free(filename_w); +#endif + return result; +} + +#endif diff --git a/libretro-common/include/compat/unlink_utf8.h b/libretro-common/include/compat/unlink_utf8.h new file mode 100644 index 0000000000..59a3d4c9f9 --- /dev/null +++ b/libretro-common/include/compat/unlink_utf8.h @@ -0,0 +1,23 @@ +#ifndef __UNLINK_UTF8_H +#define __UNLINK_UTF8_H + +#include "boolean.h" + +#ifdef _WIN32 + +#if __cplusplus +extern "C" +{ +#endif + +bool unlink_utf8(const char * filename); + +#if __cplusplus +} +#endif + +#else +#include +#define unlink_utf8 unlink +#endif +#endif diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 23d511aabd..319ee1f875 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -172,6 +172,9 @@ default_sublabel_macro(action_bind_sublabel_savestate_auto_index, MENU_ default_sublabel_macro(action_bind_sublabel_block_sram_overwrite, MENU_ENUM_SUBLABEL_BLOCK_SRAM_OVERWRITE) default_sublabel_macro(action_bind_sublabel_fastforward_ratio, MENU_ENUM_SUBLABEL_FASTFORWARD_RATIO) default_sublabel_macro(action_bind_sublabel_slowmotion_ratio, MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO) +default_sublabel_macro(action_bind_sublabel_run_ahead_enabled, MENU_ENUM_SUBLABEL_RUN_AHEAD_ENABLED) +default_sublabel_macro(action_bind_sublabel_run_ahead_secondary_instance, MENU_ENUM_SUBLABEL_RUN_AHEAD_SECONDARY_INSTANCE) +default_sublabel_macro(action_bind_sublabel_run_ahead_frames, MENU_ENUM_SUBLABEL_RUN_AHEAD_FRAMES) default_sublabel_macro(action_bind_sublabel_rewind, MENU_ENUM_SUBLABEL_REWIND_ENABLE) default_sublabel_macro(action_bind_sublabel_rewind_granularity, MENU_ENUM_SUBLABEL_REWIND_GRANULARITY) default_sublabel_macro(action_bind_sublabel_libretro_log_level, MENU_ENUM_SUBLABEL_LIBRETRO_LOG_LEVEL) @@ -1109,6 +1112,15 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_SLOWMOTION_RATIO: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_slowmotion_ratio); break; + case MENU_ENUM_LABEL_RUN_AHEAD_ENABLED: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_run_ahead_enabled); + break; + case MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_run_ahead_secondary_instance); + break; + case MENU_ENUM_LABEL_RUN_AHEAD_FRAMES: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_run_ahead_frames); + break; case MENU_ENUM_LABEL_FASTFORWARD_RATIO: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_fastforward_ratio); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index bdc3c8df3f..768ea75d29 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -4963,6 +4963,15 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_SLOWMOTION_RATIO, PARSE_ONLY_FLOAT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_RUN_AHEAD_ENABLED, + PARSE_ONLY_BOOL, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, + PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, + PARSE_ONLY_BOOL, false); if (settings->bools.menu_show_advanced_settings) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 6f17dcfeae..55cda7d72d 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -4834,6 +4834,51 @@ static bool setting_append_list( general_read_handler); menu_settings_list_current_add_range(list, list_info, 1, 10, 0.1, true, true); + CONFIG_BOOL( + list, list_info, + &settings->bools.run_ahead_enabled, + MENU_ENUM_LABEL_RUN_AHEAD_ENABLED, + MENU_ENUM_LABEL_VALUE_RUN_AHEAD_ENABLED, + false, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + + CONFIG_UINT( + list, list_info, + &settings->uints.run_ahead_frames, + MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, + MENU_ENUM_LABEL_VALUE_RUN_AHEAD_FRAMES, + 1, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + menu_settings_list_current_add_range(list, list_info, 1, 6, 1, true, true); + + CONFIG_BOOL( + list, list_info, + &settings->bools.run_ahead_secondary_instance, + MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, + MENU_ENUM_LABEL_VALUE_RUN_AHEAD_SECONDARY_INSTANCE, + false, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + CONFIG_BOOL( list, list_info, &settings->bools.menu_throttle_framerate, diff --git a/msg_hash.h b/msg_hash.h index 181814deb1..59c330a01a 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1262,6 +1262,9 @@ enum msg_hash_enums MENU_LABEL(THUMBNAILS_DIRECTORY), MENU_LABEL(SLOWMOTION_RATIO), + MENU_LABEL(RUN_AHEAD_ENABLED), + MENU_LABEL(RUN_AHEAD_SECONDARY_INSTANCE), + MENU_LABEL(RUN_AHEAD_FRAMES), MENU_LABEL(TURBO), /* Privacy settings */ diff --git a/retroarch.c b/retroarch.c index ed8ef60679..ca6187a52c 100644 --- a/retroarch.c +++ b/retroarch.c @@ -118,6 +118,8 @@ #include "command.h" +#include "runahead/run_ahead.h" + #define _PSUPP(var, name, desc) printf(" %s:\n\t\t%s: %s\n", name, desc, _##var##_supp ? "yes" : "no") #define FAIL_CPU(simd_type) do { \ @@ -3248,7 +3250,15 @@ int runloop_iterate(unsigned *sleep_ms) if ((settings->uints.video_frame_delay > 0) && !input_nonblock_state) retro_sleep(settings->uints.video_frame_delay); - core_run(); + /* Run Ahead Feature replaces the call to core_run in this loop */ + if (settings->bools.run_ahead_enabled && settings->uints.run_ahead_frames > 0) + { + run_ahead(settings->uints.run_ahead_frames, settings->bools.run_ahead_secondary_instance); + } + else + { + core_run(); + } #ifdef HAVE_CHEEVOS if (runloop_check_cheevos()) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c new file mode 100644 index 0000000000..4d0f394d60 --- /dev/null +++ b/runahead/copy_load_info.c @@ -0,0 +1,177 @@ +#include "libretro.h" +#include "mem_util.h" +#include "core.h" +#include +#include +#include "lists/string_list.h" + +retro_ctx_load_content_info_t *load_content_info; +enum rarch_core_type last_core_type; + +static void free_retro_game_info(struct retro_game_info *dest) +{ + if (dest == NULL) return; + FREE(dest->path); + FREE(dest->data); + FREE(dest->meta); +} + +static struct retro_game_info* clone_retro_game_info(const struct retro_game_info *src) +{ + struct retro_game_info *dest; + if (src == NULL) return NULL; + dest = (struct retro_game_info*)malloc_zero(sizeof(struct retro_game_info)); + dest->path = strcpy_alloc(src->path); + dest->data = memcpy_alloc(src->data, src->size); + dest->size = src->size; + dest->meta = strcpy_alloc(src->meta); + return dest; +} + +static void free_string_list(struct string_list *dest) +{ + int i; + if (dest == NULL) return; + for (i = 0; i < dest->size; i++) + { + FREE(dest->elems[i].data); + } + FREE(dest->elems); +} + +static struct string_list* clone_string_list(const struct string_list *src) +{ + int i; + struct string_list *dest; + if (src == NULL) return NULL; + dest = (struct string_list*)malloc_zero(sizeof(struct string_list)); + dest->size = src->size; + dest->cap = src->cap; + dest->elems = (struct string_list_elem*)malloc_zero(sizeof(struct string_list_elem) * dest->size); + for (i = 0; i < src->size; i++) + { + dest->elems[i].data = strcpy_alloc(src->elems[i].data); + dest->elems[i].attr = src->elems[i].attr; + } + return dest; +} + +#if 0 +/* for cloning the Special field, however, attempting to use this feature crashes retroarch */ +static void free_retro_subsystem_memory_info(struct retro_subsystem_memory_info *dest) +{ + if (dest == NULL) return; + FREE(dest->extension); +} + +static void clone_retro_subsystem_memory_info(struct retro_subsystem_memory_info* dest, const struct retro_subsystem_memory_info *src) +{ + dest->extension = strcpy_alloc(src->extension); + dest->type = src->type; +} + +static void free_retro_subsystem_rom_info(struct retro_subsystem_rom_info *dest) +{ + int i; + if (dest == NULL) return; + FREE(dest->desc); + FREE(dest->valid_extensions); + for (i = 0; i < dest->num_memory; i++) + { + free_retro_subsystem_memory_info((struct retro_subsystem_memory_info*)&dest->memory[i]); + } + FREE(dest->memory); +} + +static void clone_retro_subsystem_rom_info(struct retro_subsystem_rom_info *dest, const struct retro_subsystem_rom_info *src) +{ + int i; + retro_subsystem_memory_info *memory; + dest->need_fullpath = src->need_fullpath; + dest->block_extract = src->block_extract; + dest->required = src->required; + dest->num_memory = src->num_memory; + dest->desc = strcpy_alloc(src->desc); + dest->valid_extensions = strcpy_alloc(src->valid_extensions); + memory = (struct retro_subsystem_memory_info*)malloc_zero(dest->num_memory * sizeof(struct retro_subsystem_memory_info)); + dest->memory = memory; + for (i = 0; i < dest->num_memory; i++) + { + clone_retro_subsystem_memory_info(&memory[i], &src->memory[i]); + } +} + +static void free_retro_subsystem_info(struct retro_subsystem_info *dest) +{ + int i; + if (dest == NULL) return; + FREE(dest->desc); + FREE(dest->ident); + for (i = 0; i < dest->num_roms; i++) + { + free_retro_subsystem_rom_info((struct retro_subsystem_rom_info*)&dest->roms[i]); + } + FREE(dest->roms); +} + +static retro_subsystem_info* clone_retro_subsystem_info(struct const retro_subsystem_info *src) +{ + int i; + retro_subsystem_info *dest; + retro_subsystem_rom_info *roms; + if (src == NULL) return NULL; + dest = (struct retro_subsystem_info*)malloc_zero(sizeof(struct retro_subsystem_info)); + dest->desc = strcpy_alloc(src->desc); + dest->ident = strcpy_alloc(src->ident); + dest->num_roms = src->num_roms; + dest->id = src->id; + + roms = (struct retro_subsystem_rom_info*)malloc_zero(src->num_roms * sizeof(struct retro_subsystem_rom_info)); + dest->roms = roms; + for (i = 0; i < src->num_roms; i++) + { + clone_retro_subsystem_rom_info(&roms[i], &src->roms[i]); + } + return dest; +} +#endif + +static void free_retro_ctx_load_content_info(struct retro_ctx_load_content_info *dest) +{ + if (dest == NULL) return; + free_retro_game_info(dest->info); + free_string_list((struct string_list*)dest->content); + FREE(dest->info); + FREE(dest->content); +#if 0 + free_retro_subsystem_info((retro_subsystem_info*)dest->special); + FREE(dest->special); +#endif +} + +static struct retro_ctx_load_content_info* clone_retro_ctx_load_content_info(const struct retro_ctx_load_content_info *src) +{ + struct retro_ctx_load_content_info *dest; + if (src == NULL || src->special != NULL) return NULL; /* refuse to deal with the Special field */ + + dest = (struct retro_ctx_load_content_info*)malloc_zero(sizeof(struct retro_ctx_load_content_info)); + dest->info = clone_retro_game_info(src->info); + dest->content = clone_string_list(src->content); + dest->special = NULL; +#if 0 + dest->special = clone_retro_subsystem_info(src->special); +#endif + return dest; +} + +void set_load_content_info(const retro_ctx_load_content_info_t *ctx) +{ + free_retro_ctx_load_content_info(load_content_info); + free(load_content_info); + load_content_info = clone_retro_ctx_load_content_info(ctx); +} + +void set_last_core_type(enum rarch_core_type type) +{ + last_core_type = type; +} diff --git a/runahead/copy_load_info.h b/runahead/copy_load_info.h new file mode 100644 index 0000000000..9223ff4748 --- /dev/null +++ b/runahead/copy_load_info.h @@ -0,0 +1,16 @@ +#ifndef __COPY_LOAD_INFO_H__ +#define __COPY_LOAD_INFO_H__ + +#include "retro_common_api.h" +#include "libretro.h" +#include "core.h" +#include "boolean.h" + +RETRO_BEGIN_DECLS + +void set_load_content_info(const retro_ctx_load_content_info_t *ctx); +void set_last_core_type(enum rarch_core_type type); + +RETRO_END_DECLS + +#endif diff --git a/runahead/dirty_input.c b/runahead/dirty_input.c new file mode 100644 index 0000000000..eab75dec9c --- /dev/null +++ b/runahead/dirty_input.c @@ -0,0 +1,166 @@ +#include "core.h" +#include "boolean.h" +#include "mylist.h" +#include "dynamic.h" +#include "mem_util.h" + +bool input_is_dirty; +static MyList *inputStateList; + +typedef struct InputListElement_t +{ + unsigned port; + unsigned device; + unsigned index; + int16_t state[36]; +} InputListElement; + +typedef struct retro_core_t _retro_core_t; +extern _retro_core_t current_core; +extern struct retro_callbacks retro_ctx; + +typedef bool(*LoadStateFunction)(const void*, size_t); + +static function_t retro_reset_callback_original = NULL; +static LoadStateFunction retro_unserialize_callback_original = NULL; +static retro_input_state_t input_state_callback_original; + +static void reset_hook(void); +static bool unserialze_hook(const void *buf, size_t size); + +static void* InputListElementConstructor(void) +{ + void *ptr; + const int size = sizeof(InputListElement); + ptr = malloc_zero(size); + return ptr; +} + +static void input_state_destory(void) +{ + mylist_destroy(&inputStateList); +} + +static void input_state_setlast(unsigned port, unsigned device, unsigned index, unsigned id, int16_t value) +{ + int i; + InputListElement *element; + + if (inputStateList == NULL) + { + mylist_create(&inputStateList, 16, InputListElementConstructor, free); + } + + /* find list item */ + for (i = 0; i < inputStateList->size; i++) + { + element = (InputListElement*)inputStateList->data[i]; + if (element->port == port && element->device == device && element->index == index) + { + element->state[id] = value; + return; + } + } + element = (InputListElement*)mylist_add_element(inputStateList); + element->port = port; + element->device = device; + element->index = index; + element->state[id] = value; +} + +static int16_t input_state_getlast(unsigned port, unsigned device, unsigned index, unsigned id) +{ + int i; + InputListElement *element; + if (inputStateList == NULL) + { + return 0; + } + /* find list item */ + for (i = 0; i < inputStateList->size; i++) + { + element = (InputListElement*)inputStateList->data[i]; + if (element->port == port && element->device == device && element->index == index) + { + return element->state[id]; + } + } + return 0; +} + +static int16_t input_state_with_logging(unsigned port, unsigned device, unsigned index, unsigned id) +{ + if (input_state_callback_original != NULL) + { + int16_t result = input_state_callback_original(port, device, index, id); + int16_t lastInput = input_state_getlast(port, device, index, id); + if (result != lastInput) + { + input_is_dirty = true; + } + input_state_setlast(port, device, index, id, result); + return result; + } + return 0; +} + +static void reset_hook(void) +{ + input_is_dirty = true; + if (retro_reset_callback_original) + { + retro_reset_callback_original(); + } +} + +static bool unserialze_hook(const void *buf, size_t size) +{ + input_is_dirty = true; + if (retro_unserialize_callback_original) + { + return retro_unserialize_callback_original(buf, size); + } + return false; +} + +void add_input_state_hook(void) +{ + if (input_state_callback_original == NULL) + { + input_state_callback_original = retro_ctx.state_cb; + retro_ctx.state_cb = input_state_with_logging; + current_core.retro_set_input_state(retro_ctx.state_cb); + } + if (retro_reset_callback_original == NULL) + { + retro_reset_callback_original = current_core.retro_reset; + current_core.retro_reset = reset_hook; + } + if (retro_unserialize_callback_original == NULL) + { + retro_unserialize_callback_original = current_core.retro_unserialize; + current_core.retro_unserialize = unserialze_hook; + } +} + +void remove_input_state_hook(void) +{ + if (input_state_callback_original != NULL) + { + retro_ctx.state_cb = input_state_callback_original; + current_core.retro_set_input_state(retro_ctx.state_cb); + input_state_callback_original = NULL; + input_state_destory(); + } + if (retro_reset_callback_original != NULL) + { + current_core.retro_reset = retro_reset_callback_original; + retro_reset_callback_original = NULL; + } + if (retro_unserialize_callback_original != NULL) + { + current_core.retro_unserialize = retro_unserialize_callback_original; + retro_unserialize_callback_original = NULL; + } +} + diff --git a/runahead/dirty_input.h b/runahead/dirty_input.h new file mode 100644 index 0000000000..12623d2b02 --- /dev/null +++ b/runahead/dirty_input.h @@ -0,0 +1,15 @@ +#ifndef __DIRTY_INPUT_H___ +#define __DIRTY_INPUT_H___ + +#include "retro_common_api.h" +#include "boolean.h" + +RETRO_BEGIN_DECLS + +extern bool input_is_dirty; +void add_input_state_hook(void); +void remove_input_state_hook(void); + +RETRO_END_DECLS + +#endif diff --git a/runahead/mem_util.c b/runahead/mem_util.c new file mode 100644 index 0000000000..5d130e7425 --- /dev/null +++ b/runahead/mem_util.c @@ -0,0 +1,95 @@ +#include "mem_util.h" + +void *malloc_zero(size_t size) +{ + void *ptr; + ptr = malloc(size); + memset(ptr, 0, size); + return ptr; +} + +void free_str(char **str_p) +{ + free_ptr((void**)str_p); +} + +void free_ptr(void **data_p) +{ + if (data_p == NULL) + { + return; + } + if (*data_p == NULL) + { + return; + } + free(*data_p); + *data_p = NULL; +} + +void *memcpy_alloc(const void *src, size_t size) +{ + void *result; + result = malloc(size); + memcpy(result, src, size); + return result; +} + +char *strcpy_alloc(const char *sourceStr) +{ + size_t len; + char *result; + if (sourceStr == NULL) + { + len = 0; + } + else + { + len = strlen(sourceStr); + } + if (len == 0) + { + return NULL; + } + result = (char*)malloc(len + 1); + strcpy(result, sourceStr); + return result; +} + +char *strcpy_alloc_force(const char *sourceStr) +{ + char *result; + result = strcpy_alloc(sourceStr); + if (result == NULL) + { + result = (char*)malloc_zero(1); + } + return result; +} + +void strcat_alloc(char ** destStr_p, const char *appendStr) +{ + size_t len1, len2, newLen; + char *destStr; + + destStr = *destStr_p; + + if (destStr == NULL) + { + destStr = strcpy_alloc_force(appendStr); + *destStr_p = destStr; + return; + } + + if (appendStr == NULL) + { + return; + } + + len1 = strlen(destStr); + len2 = strlen(appendStr); + newLen = len1 + len2 + 1; + destStr = (char*)realloc(destStr, newLen); + *destStr_p = destStr; + strcpy(destStr + len1, appendStr); +} diff --git a/runahead/mem_util.h b/runahead/mem_util.h new file mode 100644 index 0000000000..9c97e00222 --- /dev/null +++ b/runahead/mem_util.h @@ -0,0 +1,26 @@ +#ifndef __MEM_UTIL__ +#define __MEM_UTIL__ + +#include "retro_common_api.h" +#include "boolean.h" + +#include +#include +#include + +#define FREE(xxxx) if ((xxxx) != NULL) { free((void*)(xxxx)); } (xxxx) = NULL + +RETRO_BEGIN_DECLS + +void *malloc_zero(size_t size); +void free_str(char **str_p); +void free_ptr(void **data_p); +char *strcpy_alloc(const char *sourceStr); +char *strcpy_alloc_force(const char *sourceStr); +void strcat_alloc(char ** destStr_p, const char *appendStr); +void *memcpy_alloc(const void *src, size_t size); + +RETRO_END_DECLS + +#endif + diff --git a/runahead/mylist.c b/runahead/mylist.c new file mode 100644 index 0000000000..e5e1d85109 --- /dev/null +++ b/runahead/mylist.c @@ -0,0 +1,151 @@ +#include "mylist.h" +#include +#include +#include "mem_util.h" + +void mylist_resize(MyList *list, int newSize, bool runConstructor) +{ + int newCapacity; + int oldSize; + int i; + void *element; + if (newSize < 0) newSize = 0; + if (list == NULL) return; + newCapacity = newSize; + oldSize = list->size; + if (newSize == oldSize) return; + if (newSize > list->capacity) + { + if (newCapacity < list->capacity * 2) + { + newCapacity = list->capacity * 2; + } + /* try to realloc */ + list->data = (void**)realloc((void*)list->data, newCapacity * sizeof(void*)); + for (i = list->capacity; i < newCapacity; i++) + { + list->data[i] = NULL; + } + list->capacity = newCapacity; + } + if (newSize <= list->size) + { + for (i = newSize; i < list->size; i++) + { + element = list->data[i]; + if (element != NULL) + { + list->Destructor(element); + list->data[i] = NULL; + } + } + } + else + { + for (i = list->size; i < newSize; i++) + { + if (runConstructor) + { + list->data[i] = list->Constructor(); + } + else + { + list->data[i] = NULL; + } + } + } + list->size = newSize; +} + +void *mylist_add_element(MyList *list) +{ + int oldSize; + if (list == NULL) return NULL; + oldSize = list->size; + mylist_resize(list, oldSize + 1, true); + return list->data[oldSize]; +} + +void mylist_create(MyList **list_p, int initialCapacity, constructor_t constructor, destructor_t destructor) +{ + MyList *list; + if (list_p == NULL) return; + if (initialCapacity < 0) initialCapacity = 0; + list = *list_p; + if (list != NULL) + { + mylist_destroy(list_p); + } + list = (MyList*)malloc(sizeof(MyList)); + *list_p = list; + list->size = 0; + list->Constructor = constructor; + list->Destructor = destructor; + if (initialCapacity > 0) + { + list->data = (void**)malloc_zero(initialCapacity * sizeof(void*)); + list->capacity = initialCapacity; + } + else + { + list->data = NULL; + list->capacity = 0; + } +} + +void mylist_destroy(MyList **list_p) +{ + if (list_p == NULL) return; + MyList *list; + list = *list_p; + if (list != NULL) + { + mylist_resize(list, 0, false); + free(list->data); + free(list); + *list_p = NULL; + } +} + +void mylist_assign(MyList *list, int index, void *value) +{ + void *oldElement; + if (index < 0 || index >= list->size) + { + return; + } + oldElement = list->data[index]; + list->Destructor(oldElement); + list->data[index] = value; +} + +void mylist_remove_at(MyList *list, int index) +{ + int i; + + if (index < 0 || index >= list->size) + { + return; + } + mylist_assign(list, index, NULL); + for (i = index + 1; i < list->size; i++) + { + list->data[i - 1] = list->data[i]; + } + list->size--; + list->data[list->size] = NULL; +} + +void mylist_pop_front(MyList *list) +{ + mylist_remove_at(list, 0); +} + +void mylist_push_back(MyList *list, void *value) +{ + int oldSize; + if (list == NULL) return; + oldSize = list->size; + mylist_resize(list, oldSize + 1, false); + list->data[oldSize] = value; +} diff --git a/runahead/mylist.h b/runahead/mylist.h new file mode 100644 index 0000000000..08438378f1 --- /dev/null +++ b/runahead/mylist.h @@ -0,0 +1,32 @@ +#ifndef __MYLIST_H__ +#define __MYLIST_H__ + +#include "retro_common_api.h" +#include "boolean.h" + +RETRO_BEGIN_DECLS + +typedef void* (*constructor_t)(void); +typedef void(*destructor_t)(void*); + +typedef struct MyList_t +{ + void **data; + int capacity; + int size; + constructor_t Constructor; + destructor_t Destructor; +} MyList; + +void *mylist_add_element(MyList *list); +void mylist_resize(MyList *list, int newSize, bool runConstructor); +void mylist_create(MyList **list_p, int initialCapacity, constructor_t constructor, destructor_t destructor); +void mylist_destroy(MyList **list_p); +void mylist_assign(MyList *list, int index, void *value); +void mylist_remove_at(MyList *list, int index); +void mylist_pop_front(MyList *list); + +RETRO_END_DECLS + +#endif + diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c new file mode 100644 index 0000000000..1e5e673974 --- /dev/null +++ b/runahead/run_ahead.c @@ -0,0 +1,425 @@ +#include "core.h" +#include "dynamic.h" +#include "audio/audio_driver.h" +#include "gfx/video_driver.h" +#include "boolean.h" +#include +#include +#include +#include "dirty_input.h" +#include "mylist.h" +#include "secondary_core.h" + +static void *runahead_save_state_alloc(void); +static void runahead_save_state_free(void *state); +static void runahead_save_state_list_init(size_t saveStateSize); +static void runahead_save_state_list_destroy(void); +static void runahead_save_state_list_rotate(void); + +static void add_hooks(void); +static void remove_hooks(void); +static void deinit_hook(void); +static void unload_hook(void); + +static void runahead_clear_variables(void); +static void runahead_check_for_gui(void); +static void runahead_error(void); +static bool runahead_create(void); +static bool runahead_save_state(void); +static bool runahead_load_state(void); +static bool runahead_load_state_secondary(void); +static bool runahead_run_secondary(void); +static void runahead_suspend_audio(void); +static void runahead_resume_audio(void); +static void runahead_suspend_video(void); +static void runahead_resume_video(void); +void run_ahead(int runAheadCount, bool useSecondary); +void runahead_destroy(void); + +static void runahead_destroy(void); +static bool runahead_create(void); + +static size_t runahead_save_state_size = -1; + +/* Save State List for Run Ahead */ +static MyList *runahead_save_state_list; + +static void *runahead_save_state_alloc(void) +{ + retro_ctx_serialize_info_t *savestate; + savestate = (retro_ctx_serialize_info_t*)malloc(sizeof(retro_ctx_serialize_info_t)); + savestate->size = runahead_save_state_size; + if (runahead_save_state_size > 0 && runahead_save_state_size != -1) + { + savestate->data = malloc(runahead_save_state_size); + savestate->data_const = savestate->data; + } + else + { + savestate->data = NULL; + savestate->data_const = NULL; + savestate->size = 0; + } + return savestate; +} + +static void runahead_save_state_free(void *state) +{ + retro_ctx_serialize_info_t *savestate; + savestate = (retro_ctx_serialize_info_t*)state; + if (savestate == NULL) return; + free(savestate->data); + free(savestate); +} + +static void runahead_save_state_list_init(size_t saveStateSize) +{ + runahead_save_state_size = saveStateSize; + mylist_create(&runahead_save_state_list, 16, runahead_save_state_alloc, runahead_save_state_free); +} + +static void runahead_save_state_list_destroy(void) +{ + mylist_destroy(&runahead_save_state_list); +} + +static void runahead_save_state_list_rotate(void) +{ + int i; + void *element; + void *firstElement; + firstElement = runahead_save_state_list->data[0]; + for (i = 1; i < runahead_save_state_list->size; i++) + { + runahead_save_state_list->data[i - 1] = runahead_save_state_list->data[i - 1]; + } + runahead_save_state_list->data[runahead_save_state_list->size - 1] = firstElement; +} + +/* Hooks - Hooks to cleanup, and add dirty input hooks */ + +static function_t originalRetroDeinit = NULL; +static function_t originalRetroUnload = NULL; + +typedef struct retro_core_t _retro_core_t; +extern _retro_core_t current_core; +extern struct retro_callbacks retro_ctx; + +static void deinit_hook(void); +static void unload_hook(void); + +static void add_hooks(void) +{ + if (originalRetroDeinit == NULL) + { + originalRetroDeinit = current_core.retro_deinit; + current_core.retro_deinit = deinit_hook; + } + if (originalRetroUnload == NULL) + { + originalRetroUnload = current_core.retro_unload_game; + current_core.retro_unload_game = unload_hook; + } + add_input_state_hook(); +} + +static void remove_hooks(void) +{ + if (originalRetroDeinit != NULL) + { + current_core.retro_deinit = originalRetroDeinit; + originalRetroDeinit = NULL; + } + if (originalRetroUnload != NULL) + { + current_core.retro_unload_game = originalRetroUnload; + originalRetroUnload = NULL; + } + remove_input_state_hook(); +} + +static void deinit_hook(void) +{ + remove_hooks(); + runahead_destroy(); + secondary_core_destroy(); + if (current_core.retro_deinit) + { + current_core.retro_deinit(); + } +} + +static void unload_hook(void) +{ + remove_hooks(); + runahead_destroy(); + secondary_core_destroy(); + if (current_core.retro_unload_game) + { + current_core.retro_unload_game(); + } +} + +/* Runahead Code */ + +static bool runahead_video_driver_is_active = true; +static bool runahead_available = true; +static bool runahead_secondary_core_available = true; +static bool runahead_force_input_dirty = true; +static uint64_t runahead_last_frame_count = 0; + +static void runahead_clear_variables(void) +{ + runahead_save_state_size = -1; + runahead_video_driver_is_active = true; + runahead_available = true; + runahead_secondary_core_available = true; + runahead_force_input_dirty = true; + runahead_last_frame_count = 0; +} + +static void runahead_check_for_gui(void) +{ + /* Hack: If we were in the GUI, force a resync. */ + bool is_dirty; + bool is_alive, is_focused; + uint64_t frame_count; + video_driver_get_status(&frame_count, &is_alive, &is_focused); + if (frame_count != runahead_last_frame_count + 1) + { + runahead_force_input_dirty = true; + } + runahead_last_frame_count = frame_count; +} + +void run_ahead(int runAheadCount, bool useSecondary) +{ + int frameNumber; + bool okay; + bool lastFrame; + bool suspendedFrame; + + if (runAheadCount <= 0 || !runahead_available) + { + core_run(); + runahead_force_input_dirty = true; + return; + } + + if (runahead_save_state_size == -1) + { + if (!runahead_create()) + { + /*runloop_msg_queue_push("RunAhead has been disabled because the core does not support savestates", 1, 180, true);*/ + core_run(); + runahead_force_input_dirty = true; + return; + } + } + + runahead_check_for_gui(); + + if (!useSecondary || !HAVE_DYNAMIC || !runahead_secondary_core_available) + { + /* TODO: multiple savestates for higher performance when not using secondary core */ + for (frameNumber = 0; frameNumber <= runAheadCount; frameNumber++) + { + lastFrame = frameNumber == runAheadCount; + suspendedFrame = !lastFrame; + if (suspendedFrame) + { + runahead_suspend_audio(); + runahead_suspend_video(); + } + if (frameNumber == 0) + { + core_run(); + } + else + { + core_run_no_input_polling(); + } + if (suspendedFrame) + { + runahead_resume_video(); + runahead_resume_audio(); + } + if (frameNumber == 0) + { + if (!runahead_save_state()) + { + /*runloop_msg_queue_push("RunAhead has been disabled due to save state failure", 1, 180, true);*/ + return; + } + } + if (lastFrame) + { + if (!runahead_load_state()) + { + /*runloop_msg_queue_push("RunAhead has been disabled due to load state failure", 1, 180, true);*/ + return; + } + } + } + } + else + { +#if HAVE_DYNAMIC + /* run main core with video suspended */ + runahead_suspend_video(); + core_run(); + runahead_resume_video(); + + if (input_is_dirty || runahead_force_input_dirty) + { + input_is_dirty = false; + if (!runahead_save_state()) + { + return; + } + if (!runahead_load_state_secondary()) + { + /*runloop_msg_queue_push("Could not create a secondary core. RunAhead will only use the main core now.", 1, 180, true);*/ + return; + } + for (int frameCount = 0; frameCount < runAheadCount - 1; frameCount++) + { + runahead_suspend_video(); + runahead_suspend_audio(); + okay = runahead_run_secondary(); + runahead_resume_audio(); + runahead_resume_video(); + if (!okay) + { + /*runloop_msg_queue_push("Could not create a secondary core. RunAhead will only use the main core now.", 1, 180, true);*/ + return; + } + } + } + runahead_suspend_audio(); + okay = runahead_run_secondary(); + runahead_resume_audio(); + if (!okay) + { + /*runloop_msg_queue_push("Could not create a secondary core. RunAhead will only use the main core now.", 1, 180, true);*/ + return; + } +#endif + } + runahead_force_input_dirty = false; +} + +static void runahead_error(void) +{ + runahead_available = false; + runahead_save_state_list_destroy(); + remove_hooks(); + runahead_save_state_size = 0; +} + +static bool runahead_create(void) +{ + /* get savestate size and allocate buffer */ + retro_ctx_size_info_t info; + core_serialize_size(&info); + + runahead_save_state_list_init(info.size); + runahead_video_driver_is_active = video_driver_is_active(); + + if (runahead_save_state_size == 0 || runahead_save_state_size == -1) + { + runahead_error(); + return false; + } + add_hooks(); + runahead_force_input_dirty = true; + mylist_resize(runahead_save_state_list, 1, true); + return true; +} + +static bool runahead_save_state(void) +{ + bool okay; + retro_ctx_serialize_info_t *serialize_info; + serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; + okay = core_serialize(serialize_info); + if (!okay) + { + runahead_error(); + } + return okay; +} + +static bool runahead_load_state(void) +{ + bool okay; + bool lastDirty; + retro_ctx_serialize_info_t *serialize_info; + serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; + lastDirty = input_is_dirty; + okay = core_unserialize(serialize_info); + input_is_dirty = lastDirty; + if (!okay) + { + runahead_error(); + } + return okay; +} + +static bool runahead_load_state_secondary(void) +{ + bool okay; + retro_ctx_serialize_info_t *serialize_info; + serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; + okay = secondary_core_deserialize(serialize_info->data_const, serialize_info->size); + if (!okay) + { + runahead_secondary_core_available = false; + } + return okay; +} + +static bool runahead_run_secondary(void) +{ + bool okay; + okay = secondary_core_run_no_input_polling(); + if (!okay) + { + runahead_secondary_core_available = false; + } + return okay; +} + +static void runahead_suspend_audio(void) +{ + audio_driver_suspend(); +} + +static void runahead_resume_audio(void) +{ + audio_driver_resume(); +} + +static void runahead_suspend_video(void) +{ + video_driver_unset_active(); +} + +static void runahead_resume_video(void) +{ + if (runahead_video_driver_is_active) + { + video_driver_set_active(); + } + else + { + video_driver_unset_active(); + } +} + +void runahead_destroy(void) +{ + runahead_save_state_list_destroy(); + remove_hooks(); + runahead_clear_variables(); +} diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c new file mode 100644 index 0000000000..58cc6ff47a --- /dev/null +++ b/runahead/secondary_core.c @@ -0,0 +1,487 @@ +#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC + +#include +#include +#include + +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) +#ifndef LEGACY_WIN32 +#define LEGACY_WIN32 +#endif +#endif + +#include "mem_util.h" +#include "boolean.h" +#include "encodings/utf.h" +#include "compat/fopen_utf8.h" +#include "compat/unlink_utf8.h" +#include "dynamic/dylib.h" +#include "dynamic.h" +#include "core.h" +#include "file/file_path.h" +#include "paths.h" +#include "content.h" + +static int port_map[16]; + +typedef struct retro_core_t _retro_core_t; +typedef struct retro_callbacks retro_callbacks_t; + +static char *secondary_library_path; +static dylib_t secondary_module; +static _retro_core_t secondary_core; +static struct retro_callbacks secondary_callbacks; + +extern retro_ctx_load_content_info_t *load_content_info; +extern enum rarch_core_type last_core_type; +extern struct retro_callbacks retro_ctx; + +static char* get_temp_directory_alloc(void); +static char* copy_core_to_temp_file(void); +static void* read_file_data_alloc(const char *fileName, int *size); +static bool write_file_data(const char *fileName, const void *data, int dataSize); +static bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, int dataSize); +static void* InputListElementConstructor(void); +static void secondary_core_clear(void); +static bool secondary_core_create(void); +bool secondary_core_run_no_input_polling(void); +bool secondary_core_deserialize(const void *buffer, int size); +void secondary_core_destroy(void); +void set_last_core_type(enum rarch_core_type type); +void remember_controller_port_device(long port, long device); +void clear_controller_port_map(void); + +static void free_file(FILE **file_p); + +char* get_temp_directory_alloc(void) +{ +#ifdef _WIN32 +#ifdef LEGACY_WIN32 + DWORD pathLength; + char *path; + + pathLength = GetTempPath(0, NULL) + 1; + path = (char*)malloc(pathLength * sizeof(char)); + path[pathLength - 1] = 0; + GetTempPath(pathLength, path); + return path; +#else + DWORD pathLength; + wchar_t *wideStr; + char *path; + + pathLength = GetTempPathW(0, NULL) + 1; + wideStr = (wchar_t*)malloc(pathLength * sizeof(wchar_t)); + wideStr[pathLength - 1] = 0; + GetTempPathW(pathLength, wideStr); + + path = utf16_to_utf8_string_alloc(wideStr); + free(wideStr); + return path; +#endif +#else + char *path; + path = strcpy_alloc_force(getenv("TMPDIR"); + return path; +#endif +} + +char* copy_core_to_temp_file(void) +{ + bool okay; + const char *corePath = NULL; /* ptr to static buffer, do not need to free this */ + const char *coreBaseName = NULL; /* ptr to static buffer, do not need to free this */ + char *tempDirectory = NULL; + char *retroarchTempPath = NULL; + char *tempDllPath = NULL; + void *dllFileData = NULL; + int dllFileSize = 0; + + corePath = path_get(RARCH_PATH_CORE); + coreBaseName = path_basename(corePath); + + if (strlen(coreBaseName) == 0) + { + goto failed; + } + + tempDirectory = get_temp_directory_alloc(); + if (tempDirectory == NULL) + { + goto failed; + } + + strcat_alloc(&retroarchTempPath, tempDirectory); + strcat_alloc(&retroarchTempPath, path_default_slash()); + strcat_alloc(&retroarchTempPath, "retroarch_temp"); + strcat_alloc(&retroarchTempPath, path_default_slash()); + + okay = path_mkdir(retroarchTempPath); + if (!okay) + { + goto failed; + } + + dllFileData = read_file_data_alloc(corePath, &dllFileSize); + if (dllFileData == NULL) + { + goto failed; + } + strcat_alloc(&tempDllPath, retroarchTempPath); + strcat_alloc(&tempDllPath, coreBaseName); + okay = write_file_data(tempDllPath, dllFileData, dllFileSize); + if (!okay) + { + /* try other file names */ + okay = write_file_with_random_name(&tempDllPath, retroarchTempPath, dllFileData, dllFileSize); + if (!okay) + { + goto failed; + } + } +success: + free_str(&tempDirectory); + free_str(&retroarchTempPath); + free_ptr(&dllFileData); + return tempDllPath; + +failed: + free_str(&tempDirectory); + free_str(&retroarchTempPath); + free_str(&tempDllPath); + free_ptr(&dllFileData); + return NULL; +} + +void* read_file_data_alloc(const char *fileName, int *size) +{ + void *data = NULL; + FILE *f = NULL; + int fileSize = 0; + size_t bytesRead = 0; +#ifdef _WIN32 + int64_t fileSizeLong = 0; +#else + off64_t fileSizeLong = 0; +#endif + f = (FILE*)fopen_utf8(fileName, "rb"); + if (f == NULL) + { + goto failed; + } + fseek(f, 0, SEEK_END); +#ifdef _WIN32 + fileSizeLong = _ftelli64(f); +#else + fileSizeLong = ftello64(f); +#endif + fseek(f, 0, SEEK_SET); + /* 256MB file size limit for DLL files */ + if (fileSizeLong < 0 || fileSizeLong > 256 * 1024 * 1024) + { + goto failed; + } + fileSize = (int)fileSizeLong; + data = malloc(fileSize); + if (data == NULL) + { + goto failed; + } + bytesRead = fread(data, 1, fileSize, f); + if ((int)bytesRead != (int)fileSize) + { + goto failed; + } +success: + free_file(&f); + if (size != NULL) *size = fileSize; + return data; +failed: + free_ptr(&data); + free_file(&f); + if (size != NULL) *size = 0; + return NULL; +} + +bool write_file_data(const char *fileName, const void *data, int dataSize) +{ + bool okay = false; + FILE *f = NULL; + size_t bytesWritten = 0; + + f = (FILE*)fopen_utf8(fileName, "wb"); + if (f == NULL) goto failed; + bytesWritten = fwrite(data, 1, dataSize, f); + if (bytesWritten != dataSize) + { + goto failed; + } +success: + free_file(&f); + return true; +failed: + free_file(&f); + return false; +} + +bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, int dataSize) +{ + int extLen; + char *ext = NULL; + bool okay = false; + const int maxAttempts = 30; + const char *prefix = "tmp"; + char numberBuf[32]; + time_t timeValue = time(NULL); + unsigned int numberValue = (unsigned int)timeValue; + int number = 0; + int i; + + ext = strcpy_alloc_force(path_get_extension(*tempDllPath)); + extLen = strlen(ext); + if (extLen > 0) + { + strcat_alloc(&ext, "."); + memmove(ext + 1, ext, extLen); + ext[0] = '.'; + extLen++; + } + + + /* try up to 30 'random' filenames before giving up */ + for (i = 0; i < 30; i++) + { + numberValue = numberValue * 214013 + 2531011; + number = (numberValue >> 14) % 100000; + sprintf(numberBuf, "%05d", number); + free_str(tempDllPath); + strcat_alloc(tempDllPath, retroarchTempPath); + strcat_alloc(tempDllPath, prefix); + strcat_alloc(tempDllPath, numberBuf); + strcat_alloc(tempDllPath, ext); + okay = write_file_data(*tempDllPath, data, dataSize); + if (okay) + { + break; + } + } +success: + free_str(&ext); + return true; +failed: + free_str(&ext); + return false; +} + +void secondary_core_clear(void) +{ + secondary_library_path = NULL; + secondary_module = NULL; + memset(&secondary_core, 0, sizeof(struct retro_core_t)); +} + +bool secondary_core_create(void) +{ + long port, device; + bool contentless, is_inited; + + if (last_core_type != CORE_TYPE_PLAIN || load_content_info == NULL || load_content_info->special != NULL) + { + return false; + } + + free_str(&secondary_library_path); + secondary_library_path = copy_core_to_temp_file(); + if (secondary_library_path == NULL) + { + return false; + } + /* Load Core */ + if (init_libretro_sym_custom(CORE_TYPE_PLAIN, &secondary_core, secondary_library_path, &secondary_module)) + { + secondary_core.symbols_inited = true; + + core_set_default_callbacks(&secondary_callbacks); + secondary_core.retro_set_video_refresh(secondary_callbacks.frame_cb); + secondary_core.retro_set_audio_sample(secondary_callbacks.sample_cb); + secondary_core.retro_set_audio_sample_batch(secondary_callbacks.sample_batch_cb); + secondary_core.retro_set_input_state(secondary_callbacks.state_cb); + secondary_core.retro_set_input_poll(secondary_callbacks.poll_cb); + secondary_core.retro_set_environment(rarch_environment_cb); + + secondary_core.retro_init(); + + content_get_status(&contentless, &is_inited); + secondary_core.inited = is_inited; + + /* Load Content */ + if (load_content_info == NULL || load_content_info->special != NULL) + { + /* disabled due to crashes */ + return false; +#if 0 + secondary_core.game_loaded = secondary_core.retro_load_game_special(loadContentInfo.special->id, loadContentInfo.info, loadContentInfo.content->size); + if (!secondary_core.game_loaded) + { + secondary_core_destroy(); + return false; + } +#endif + } + else if (load_content_info->content->size > 0 && load_content_info->content->elems[0].data != NULL) + { + secondary_core.game_loaded = secondary_core.retro_load_game(load_content_info->info); + if (!secondary_core.game_loaded) + { + secondary_core_destroy(); + return false; + } + } + else if (contentless) + { + secondary_core.game_loaded = secondary_core.retro_load_game(NULL); + if (!secondary_core.game_loaded) + { + secondary_core_destroy(); + return false; + } + } + else + { + secondary_core.game_loaded = false; + } + if (!secondary_core.inited) + { + secondary_core_destroy(); + return false; + } + + for (port = 0; port < 16; port++) + { + device = port_map[port]; + if (device >= 0) + { + secondary_core.retro_set_controller_port_device(port, device); + } + } + clear_controller_port_map(); + } + else + { + return false; + } + return true; +} + +bool secondary_core_run_no_input_polling(void) +{ + bool okay; + if (secondary_module == NULL) + { + okay = secondary_core_create(); + if (!okay) + { + return false; + } + } + secondary_core.retro_run(); + return true; +} + +bool secondary_core_deserialize(const void *buffer, int size) +{ + bool okay; + if (secondary_module == NULL) + { + okay = secondary_core_create(); + if (!okay) + { + secondary_core_destroy(); + return false; + } + } + return secondary_core.retro_unserialize(buffer, size); +} + +void secondary_core_destroy(void) +{ + if (secondary_module != NULL) + { + /* unload game from core */ + if (secondary_core.retro_unload_game != NULL) + { + secondary_core.retro_unload_game(); + } + /* deinit */ + if (secondary_core.retro_deinit != NULL) + { + secondary_core.retro_deinit(); + } + memset(&secondary_core, 0, sizeof(struct retro_core_t)); + + dylib_close(secondary_module); + secondary_module = NULL; + unlink_utf8(secondary_library_path); + free_str(&secondary_library_path); + } +} + +void remember_controller_port_device(long port, long device) +{ + if (port >= 0 && port < 16) + { + port_map[port] = device; + } + if (secondary_module != NULL && secondary_core.retro_set_controller_port_device != NULL) + { + secondary_core.retro_set_controller_port_device(port, device); + } +} + +void clear_controller_port_map(void) +{ + int port; + for (port = 0; port < 16; port++) + { + port_map[port] = -1; + } +} + +static void free_file(FILE **file_p) +{ + bool result; + if (file_p == NULL) + { + return; + } + if (*file_p == NULL) + { + return; + } + result = fclose(*file_p) != 0; + *file_p = NULL; + return; +} + + +#else +#include "boolean.h" +#include "core.h" +bool secondary_core_run_no_input_polling(void) +{ + return false; +} +bool secondary_core_deserialize(const void *buffer, int size) +{ + return false; +} +void secondary_core_destroy(void) +{ + /* do nothing */ +} +void remember_controller_port_device(long port, long device) +{ + /* do nothing */ +} +#endif + diff --git a/runahead/secondary_core.h b/runahead/secondary_core.h new file mode 100644 index 0000000000..5393c1f6e0 --- /dev/null +++ b/runahead/secondary_core.h @@ -0,0 +1,19 @@ +#ifndef __SECONDARY_CORE_H__ +#define __SECONDARY_CORE_H__ + +#include "core_type.h" +#include "retro_common_api.h" +#include "boolean.h" + +RETRO_BEGIN_DECLS + +bool secondary_core_run_no_input_polling(); +bool secondary_core_deserialize(const void *buffer, int size); +void secondary_core_destroy(); +void set_last_core_type(enum rarch_core_type type); +void remember_controller_port_device(long port, long device); +void clear_controller_port_map(); + +RETRO_END_DECLS + +#endif From 361152000d90d0901548fd2fe23a2607f110c358 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Wed, 28 Mar 2018 17:14:20 -0500 Subject: [PATCH 022/517] Added the missing file --- runahead/run_ahead.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 runahead/run_ahead.h diff --git a/runahead/run_ahead.h b/runahead/run_ahead.h new file mode 100644 index 0000000000..3f4c074d8d --- /dev/null +++ b/runahead/run_ahead.h @@ -0,0 +1,14 @@ +#ifndef __RUN_AHEAD_H__ +#define __RUN_AHEAD_H__ + +#include "boolean.h" +#include "retro_common_api.h" + +RETRO_BEGIN_DECLS + +void runahead_destroy(void); +void run_ahead(int runAheadCount, bool useSecondary); + +RETRO_END_DECLS + +#endif From ff0a2d4f573c4a0f4e7715584c4b408ad908fbbe Mon Sep 17 00:00:00 2001 From: Dwedit Date: Wed, 28 Mar 2018 17:32:37 -0500 Subject: [PATCH 023/517] Add files to the makefile --- Makefile.common | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile.common b/Makefile.common index a0931b6a0e..ae667704fa 100644 --- a/Makefile.common +++ b/Makefile.common @@ -258,7 +258,14 @@ OBJ += frontend/frontend.o \ record/drivers/record_null.o \ $(LIBRETRO_COMM_DIR)/features/features_cpu.o \ performance_counters.o \ - verbosity.o + verbosity.o \ + runahead/copy_load_info.o \ + runahead/dirty_input.o \ + runahead/mem_util.o \ + runahead/mylist.o \ + runahead/run_ahead.o \ + runahead/secondary_core.o + ifeq ($(HAVE_CC_RESAMPLER), 1) From bc3d0b2acdcca8ff90dd44483947d8093a451907 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Wed, 28 Mar 2018 17:41:45 -0500 Subject: [PATCH 024/517] Fix some static declarations --- runahead/run_ahead.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 1e5e673974..c944079493 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -36,9 +36,6 @@ static void runahead_resume_video(void); void run_ahead(int runAheadCount, bool useSecondary); void runahead_destroy(void); -static void runahead_destroy(void); -static bool runahead_create(void); - static size_t runahead_save_state_size = -1; /* Save State List for Run Ahead */ From 265b0560b389a63ea9bed6f5130abea9cf0a186b Mon Sep 17 00:00:00 2001 From: bparker06 Date: Wed, 28 Mar 2018 20:16:59 -0400 Subject: [PATCH 025/517] travis: disable all d3d versions for windows --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a67a17b635..e950b2c19b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ matrix: - g++-mingw-w64-i686 - mingw-w64-i686-dev script: - - CROSS_COMPILE=i686-w64-mingw32- ./configure --disable-d3d9 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1 + - CROSS_COMPILE=i686-w64-mingw32- ./configure --disable-d3d8 --disable-d3d9 --disable-d3d10 --disable-d3d11 --disable-d3d12 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1 - compiler: mingw-x64 addons: apt: @@ -18,7 +18,7 @@ matrix: - g++-mingw-w64-x86-64 - mingw-w64-x86-64-dev script: - - CROSS_COMPILE=x86_64-w64-mingw32- ./configure --disable-d3d9 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1 + - CROSS_COMPILE=x86_64-w64-mingw32- ./configure --disable-d3d8 --disable-d3d9 --disable-d3d10 --disable-d3d11 --disable-d3d12 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1 - compiler: gcc - compiler: clang addons: From 2c882a01c12cd3104964c70f95b4037fdd6dc4e5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 14:11:52 +0200 Subject: [PATCH 026/517] Silence some Clang static analyzer warnings --- libretro-common/net/net_http.c | 1 - menu/menu_displaylist.c | 11 ++++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/libretro-common/net/net_http.c b/libretro-common/net/net_http.c index 3b25f340d9..caa6e56c93 100644 --- a/libretro-common/net/net_http.c +++ b/libretro-common/net/net_http.c @@ -138,7 +138,6 @@ void net_http_urlencode_full(char *dest, char *tmp = NULL; char url_domain[PATH_MAX_LENGTH] = {0}; char url_path[PATH_MAX_LENGTH] = {0}; - char url_encoded[PATH_MAX_LENGTH] = {0}; int count = 0; strlcpy (url_path, source, sizeof(url_path)); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index bdc3c8df3f..a3a47fb682 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3282,16 +3282,13 @@ static int menu_displaylist_parse_options_remappings( { for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) { - unsigned user = settings->uints.keymapper_port + 1; - unsigned desc_offset = retro_id; char descriptor[255]; - const struct retro_keybind *auto_bind = NULL; - const struct retro_keybind *keybind = NULL; - - keybind = &input_config_binds[settings->uints.keymapper_port][retro_id]; - auto_bind = (const struct retro_keybind*) + const struct retro_keybind *keybind = &input_config_binds[settings->uints.keymapper_port][retro_id]; + const struct retro_keybind *auto_bind = (const struct retro_keybind*) input_config_get_bind_auto(settings->uints.keymapper_port, retro_id); + descriptor[0] = '\0'; + input_config_get_bind_string(descriptor, keybind, auto_bind, sizeof(descriptor)); From 3fe6ecbce76815ee55ac71a2d74de5cc060dedf1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 14:19:24 +0200 Subject: [PATCH 027/517] More static analyzer warning fixes --- menu/drivers/menu_generic.c | 4 +--- tasks/task_overlay.c | 12 +++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 82ed2dc04e..3661d5d5e2 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -136,11 +136,9 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) file_list_get_actiondata_at_offset(selection_buf, selection) : NULL; - if (cbs->enum_idx != MSG_UNKNOWN) - { + if (cbs && cbs->enum_idx != MSG_UNKNOWN) ret = menu_hash_get_help_enum(cbs->enum_idx, menu->menu_state_msg, sizeof(menu->menu_state_msg)); - } else { unsigned type = 0; diff --git a/tasks/task_overlay.c b/tasks/task_overlay.c index 036b869030..72c6046c8d 100644 --- a/tasks/task_overlay.c +++ b/tasks/task_overlay.c @@ -327,10 +327,14 @@ static bool task_overlay_resolve_targets(struct overlay *ol, unsigned i; struct overlay *current = (struct overlay*)&ol[idx]; + if (!current) + return false; + for (i = 0; i < current->size; i++) { - ssize_t next_idx = 0; - const char *next = current->descs[i].next_index_name; + struct overlay_desc *desc = (struct overlay_desc*)¤t->descs[i]; + const char *next = desc ? desc->next_index_name : NULL; + ssize_t next_idx = (idx + 1) & size; if (!string_is_empty(next)) { @@ -343,10 +347,8 @@ static bool task_overlay_resolve_targets(struct overlay *ol, return false; } } - else - next_idx = (idx + 1) & size; - current->descs[i].next_index = (unsigned)next_idx; + desc->next_index = (unsigned)next_idx; } return true; From 2bdfd2e1d0b61490e67b90e82a578e48770e3c54 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:13:33 +0200 Subject: [PATCH 028/517] (Runahead) Cleanups / style nits / etc. --- runahead/copy_load_info.c | 157 ++++++++++++++++---------- runahead/copy_load_info.h | 9 +- runahead/dirty_input.c | 38 +++---- runahead/mem_util.c | 58 ++++------ runahead/mem_util.h | 6 +- runahead/mylist.c | 96 +++++++++------- runahead/mylist.h | 4 +- runahead/run_ahead.c | 141 +++++++++++------------ runahead/run_ahead.h | 4 +- runahead/secondary_core.c | 230 ++++++++++++++++++-------------------- runahead/secondary_core.h | 13 ++- 11 files changed, 379 insertions(+), 377 deletions(-) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c index 4d0f394d60..c58db24360 100644 --- a/runahead/copy_load_info.c +++ b/runahead/copy_load_info.c @@ -1,26 +1,32 @@ -#include "libretro.h" -#include "mem_util.h" -#include "core.h" #include #include -#include "lists/string_list.h" + +#include +#include + +#include "../core.h" +#include "mem_util.h" retro_ctx_load_content_info_t *load_content_info; enum rarch_core_type last_core_type; static void free_retro_game_info(struct retro_game_info *dest) { - if (dest == NULL) return; + if (dest == NULL) + return; FREE(dest->path); FREE(dest->data); FREE(dest->meta); } -static struct retro_game_info* clone_retro_game_info(const struct retro_game_info *src) +static struct retro_game_info* clone_retro_game_info(const + struct retro_game_info *src) { struct retro_game_info *dest; - if (src == NULL) return NULL; - dest = (struct retro_game_info*)malloc_zero(sizeof(struct retro_game_info)); + if (src == NULL) + return NULL; + dest = (struct retro_game_info*)malloc_zero( + sizeof(struct retro_game_info)); dest->path = strcpy_alloc(src->path); dest->data = memcpy_alloc(src->data, src->size); dest->size = src->size; @@ -31,23 +37,29 @@ static struct retro_game_info* clone_retro_game_info(const struct retro_game_inf static void free_string_list(struct string_list *dest) { int i; - if (dest == NULL) return; + if (dest == NULL) + return; for (i = 0; i < dest->size; i++) { FREE(dest->elems[i].data); } + FREE(dest->elems); } static struct string_list* clone_string_list(const struct string_list *src) { int i; - struct string_list *dest; - if (src == NULL) return NULL; - dest = (struct string_list*)malloc_zero(sizeof(struct string_list)); - dest->size = src->size; - dest->cap = src->cap; - dest->elems = (struct string_list_elem*)malloc_zero(sizeof(struct string_list_elem) * dest->size); + struct string_list *dest = NULL; + + if (src == NULL) + return NULL; + + dest = (struct string_list*)malloc_zero(sizeof(struct string_list)); + dest->size = src->size; + dest->cap = src->cap; + dest->elems = (struct string_list_elem*)malloc_zero(sizeof(struct string_list_elem) * dest->size); + for (i = 0; i < src->size; i++) { dest->elems[i].data = strcpy_alloc(src->elems[i].data); @@ -57,105 +69,134 @@ static struct string_list* clone_string_list(const struct string_list *src) } #if 0 -/* for cloning the Special field, however, attempting to use this feature crashes retroarch */ -static void free_retro_subsystem_memory_info(struct retro_subsystem_memory_info *dest) +/* for cloning the Special field, however, attempting + * to use this feature crashes retroarch */ +static void free_retro_subsystem_memory_info(struct + retro_subsystem_memory_info *dest) { if (dest == NULL) return; FREE(dest->extension); } -static void clone_retro_subsystem_memory_info(struct retro_subsystem_memory_info* dest, const struct retro_subsystem_memory_info *src) +static void clone_retro_subsystem_memory_info(struct + retro_subsystem_memory_info* dest, + const struct retro_subsystem_memory_info *src) { dest->extension = strcpy_alloc(src->extension); - dest->type = src->type; + dest->type = src->type; } -static void free_retro_subsystem_rom_info(struct retro_subsystem_rom_info *dest) +static void free_retro_subsystem_rom_info(struct + retro_subsystem_rom_info *dest) { int i; - if (dest == NULL) return; + if (dest == NULL) + return; + FREE(dest->desc); FREE(dest->valid_extensions); + for (i = 0; i < dest->num_memory; i++) - { - free_retro_subsystem_memory_info((struct retro_subsystem_memory_info*)&dest->memory[i]); - } + free_retro_subsystem_memory_info((struct + retro_subsystem_memory_info*)&dest->memory[i]); + FREE(dest->memory); } -static void clone_retro_subsystem_rom_info(struct retro_subsystem_rom_info *dest, const struct retro_subsystem_rom_info *src) +static void clone_retro_subsystem_rom_info(struct + retro_subsystem_rom_info *dest, + const struct retro_subsystem_rom_info *src) { int i; - retro_subsystem_memory_info *memory; - dest->need_fullpath = src->need_fullpath; - dest->block_extract = src->block_extract; - dest->required = src->required; - dest->num_memory = src->num_memory; - dest->desc = strcpy_alloc(src->desc); + retro_subsystem_memory_info *memory = NULL; + + dest->need_fullpath = src->need_fullpath; + dest->block_extract = src->block_extract; + dest->required = src->required; + dest->num_memory = src->num_memory; + dest->desc = strcpy_alloc(src->desc); dest->valid_extensions = strcpy_alloc(src->valid_extensions); - memory = (struct retro_subsystem_memory_info*)malloc_zero(dest->num_memory * sizeof(struct retro_subsystem_memory_info)); - dest->memory = memory; + + memory = (struct retro_subsystem_memory_info*)malloc_zero( + dest->num_memory * sizeof(struct retro_subsystem_memory_info)); + + dest->memory = memory; + for (i = 0; i < dest->num_memory; i++) - { clone_retro_subsystem_memory_info(&memory[i], &src->memory[i]); - } } static void free_retro_subsystem_info(struct retro_subsystem_info *dest) { int i; - if (dest == NULL) return; + + if (dest == NULL) + return; + FREE(dest->desc); FREE(dest->ident); + for (i = 0; i < dest->num_roms; i++) - { - free_retro_subsystem_rom_info((struct retro_subsystem_rom_info*)&dest->roms[i]); - } + free_retro_subsystem_rom_info((struct + retro_subsystem_rom_info*)&dest->roms[i]); + FREE(dest->roms); } -static retro_subsystem_info* clone_retro_subsystem_info(struct const retro_subsystem_info *src) +static retro_subsystem_info* clone_retro_subsystem_info(struct + const retro_subsystem_info *src) { int i; - retro_subsystem_info *dest; - retro_subsystem_rom_info *roms; - if (src == NULL) return NULL; - dest = (struct retro_subsystem_info*)malloc_zero(sizeof(struct retro_subsystem_info)); - dest->desc = strcpy_alloc(src->desc); - dest->ident = strcpy_alloc(src->ident); - dest->num_roms = src->num_roms; - dest->id = src->id; + retro_subsystem_info *dest = NULL; + retro_subsystem_rom_info *roms = NULL; + + if (src == NULL) + return NULL; + dest = (struct retro_subsystem_info*)malloc_zero( + sizeof(struct retro_subsystem_info)); + dest->desc = strcpy_alloc(src->desc); + dest->ident = strcpy_alloc(src->ident); + dest->num_roms = src->num_roms; + dest->id = src->id; + roms = (struct retro_subsystem_rom_info*) + malloc_zero(src->num_roms * sizeof(struct retro_subsystem_rom_info)); + dest->roms = roms; - roms = (struct retro_subsystem_rom_info*)malloc_zero(src->num_roms * sizeof(struct retro_subsystem_rom_info)); - dest->roms = roms; for (i = 0; i < src->num_roms; i++) - { clone_retro_subsystem_rom_info(&roms[i], &src->roms[i]); - } + return dest; } #endif -static void free_retro_ctx_load_content_info(struct retro_ctx_load_content_info *dest) +static void free_retro_ctx_load_content_info(struct + retro_ctx_load_content_info *dest) { - if (dest == NULL) return; + if (dest == NULL) + return; + free_retro_game_info(dest->info); free_string_list((struct string_list*)dest->content); FREE(dest->info); FREE(dest->content); + #if 0 free_retro_subsystem_info((retro_subsystem_info*)dest->special); FREE(dest->special); #endif } -static struct retro_ctx_load_content_info* clone_retro_ctx_load_content_info(const struct retro_ctx_load_content_info *src) +static struct retro_ctx_load_content_info +*clone_retro_ctx_load_content_info( + const struct retro_ctx_load_content_info *src) { struct retro_ctx_load_content_info *dest; - if (src == NULL || src->special != NULL) return NULL; /* refuse to deal with the Special field */ + if (src == NULL || src->special != NULL) + return NULL; /* refuse to deal with the Special field */ - dest = (struct retro_ctx_load_content_info*)malloc_zero(sizeof(struct retro_ctx_load_content_info)); - dest->info = clone_retro_game_info(src->info); + dest = (struct retro_ctx_load_content_info*)malloc_zero( + sizeof(struct retro_ctx_load_content_info)); + dest->info = clone_retro_game_info(src->info); dest->content = clone_string_list(src->content); dest->special = NULL; #if 0 diff --git a/runahead/copy_load_info.h b/runahead/copy_load_info.h index 9223ff4748..5671f5b753 100644 --- a/runahead/copy_load_info.h +++ b/runahead/copy_load_info.h @@ -1,10 +1,11 @@ #ifndef __COPY_LOAD_INFO_H__ #define __COPY_LOAD_INFO_H__ -#include "retro_common_api.h" -#include "libretro.h" -#include "core.h" -#include "boolean.h" +#include +#include +#include + +#include "../core.h" RETRO_BEGIN_DECLS diff --git a/runahead/dirty_input.c b/runahead/dirty_input.c index eab75dec9c..0c5b1b2ac4 100644 --- a/runahead/dirty_input.c +++ b/runahead/dirty_input.c @@ -1,7 +1,9 @@ -#include "core.h" -#include "boolean.h" +#include + +#include "../core.h" +#include "../dynamic.h" + #include "mylist.h" -#include "dynamic.h" #include "mem_util.h" bool input_is_dirty; @@ -30,9 +32,9 @@ static bool unserialze_hook(const void *buf, size_t size); static void* InputListElementConstructor(void) { - void *ptr; const int size = sizeof(InputListElement); - ptr = malloc_zero(size); + void *ptr = malloc_zero(size); + return ptr; } @@ -41,21 +43,21 @@ static void input_state_destory(void) mylist_destroy(&inputStateList); } -static void input_state_setlast(unsigned port, unsigned device, unsigned index, unsigned id, int16_t value) +static void input_state_setlast(unsigned port, unsigned device, + unsigned index, unsigned id, int16_t value) { int i; InputListElement *element; - if (inputStateList == NULL) - { + if (!inputStateList) mylist_create(&inputStateList, 16, InputListElementConstructor, free); - } /* find list item */ for (i = 0; i < inputStateList->size; i++) { element = (InputListElement*)inputStateList->data[i]; - if (element->port == port && element->device == device && element->index == index) + if ( element->port == port + && element->device == device && element->index == index) { element->state[id] = value; return; @@ -71,19 +73,17 @@ static void input_state_setlast(unsigned port, unsigned device, unsigned index, static int16_t input_state_getlast(unsigned port, unsigned device, unsigned index, unsigned id) { int i; - InputListElement *element; - if (inputStateList == NULL) - { + InputListElement *element = NULL; + + if (!inputStateList) return 0; - } + /* find list item */ for (i = 0; i < inputStateList->size; i++) { element = (InputListElement*)inputStateList->data[i]; if (element->port == port && element->device == device && element->index == index) - { return element->state[id]; - } } return 0; } @@ -95,9 +95,7 @@ static int16_t input_state_with_logging(unsigned port, unsigned device, unsigned int16_t result = input_state_callback_original(port, device, index, id); int16_t lastInput = input_state_getlast(port, device, index, id); if (result != lastInput) - { input_is_dirty = true; - } input_state_setlast(port, device, index, id, result); return result; } @@ -108,18 +106,14 @@ static void reset_hook(void) { input_is_dirty = true; if (retro_reset_callback_original) - { retro_reset_callback_original(); - } } static bool unserialze_hook(const void *buf, size_t size) { input_is_dirty = true; if (retro_unserialize_callback_original) - { return retro_unserialize_callback_original(buf, size); - } return false; } diff --git a/runahead/mem_util.c b/runahead/mem_util.c index 5d130e7425..7f7876f169 100644 --- a/runahead/mem_util.c +++ b/runahead/mem_util.c @@ -2,8 +2,7 @@ void *malloc_zero(size_t size) { - void *ptr; - ptr = malloc(size); + void *ptr = malloc(size); memset(ptr, 0, size); return ptr; } @@ -15,42 +14,30 @@ void free_str(char **str_p) void free_ptr(void **data_p) { - if (data_p == NULL) - { + if (!data_p || !*data_p) return; - } - if (*data_p == NULL) - { - return; - } free(*data_p); *data_p = NULL; } void *memcpy_alloc(const void *src, size_t size) { - void *result; - result = malloc(size); + void *result = malloc(size); memcpy(result, src, size); return result; } char *strcpy_alloc(const char *sourceStr) { - size_t len; - char *result; - if (sourceStr == NULL) - { - len = 0; - } - else - { + size_t len = 0; + char *result = NULL; + + if (sourceStr) len = strlen(sourceStr); - } + if (len == 0) - { return NULL; - } + result = (char*)malloc(len + 1); strcpy(result, sourceStr); return result; @@ -58,38 +45,31 @@ char *strcpy_alloc(const char *sourceStr) char *strcpy_alloc_force(const char *sourceStr) { - char *result; - result = strcpy_alloc(sourceStr); - if (result == NULL) - { + char *result = strcpy_alloc(sourceStr); + if (!result) result = (char*)malloc_zero(1); - } return result; } void strcat_alloc(char ** destStr_p, const char *appendStr) { size_t len1, len2, newLen; - char *destStr; + char *destStr = *destStr_p; - destStr = *destStr_p; - - if (destStr == NULL) + if (!destStr) { - destStr = strcpy_alloc_force(appendStr); + destStr = strcpy_alloc_force(appendStr); *destStr_p = destStr; return; } - if (appendStr == NULL) - { + if (!appendStr) return; - } - len1 = strlen(destStr); - len2 = strlen(appendStr); - newLen = len1 + len2 + 1; - destStr = (char*)realloc(destStr, newLen); + len1 = strlen(destStr); + len2 = strlen(appendStr); + newLen = len1 + len2 + 1; + destStr = (char*)realloc(destStr, newLen); *destStr_p = destStr; strcpy(destStr + len1, appendStr); } diff --git a/runahead/mem_util.h b/runahead/mem_util.h index 9c97e00222..b02f8d8b20 100644 --- a/runahead/mem_util.h +++ b/runahead/mem_util.h @@ -1,13 +1,13 @@ #ifndef __MEM_UTIL__ #define __MEM_UTIL__ -#include "retro_common_api.h" -#include "boolean.h" - #include #include #include +#include +#include + #define FREE(xxxx) if ((xxxx) != NULL) { free((void*)(xxxx)); } (xxxx) = NULL RETRO_BEGIN_DECLS diff --git a/runahead/mylist.c b/runahead/mylist.c index e5e1d85109..de92552967 100644 --- a/runahead/mylist.c +++ b/runahead/mylist.c @@ -8,24 +8,28 @@ void mylist_resize(MyList *list, int newSize, bool runConstructor) int newCapacity; int oldSize; int i; - void *element; - if (newSize < 0) newSize = 0; - if (list == NULL) return; + void *element = NULL; + if (newSize < 0) + newSize = 0; + if (!list) + return; newCapacity = newSize; - oldSize = list->size; - if (newSize == oldSize) return; + oldSize = list->size; + + if (newSize == oldSize) + return; + if (newSize > list->capacity) { if (newCapacity < list->capacity * 2) - { newCapacity = list->capacity * 2; - } + /* try to realloc */ list->data = (void**)realloc((void*)list->data, newCapacity * sizeof(void*)); + for (i = list->capacity; i < newCapacity; i++) - { list->data[i] = NULL; - } + list->capacity = newCapacity; } if (newSize <= list->size) @@ -33,7 +37,8 @@ void mylist_resize(MyList *list, int newSize, bool runConstructor) for (i = newSize; i < list->size; i++) { element = list->data[i]; - if (element != NULL) + + if (element) { list->Destructor(element); list->data[i] = NULL; @@ -45,13 +50,9 @@ void mylist_resize(MyList *list, int newSize, bool runConstructor) for (i = list->size; i < newSize; i++) { if (runConstructor) - { list->data[i] = list->Constructor(); - } else - { list->data[i] = NULL; - } } } list->size = newSize; @@ -60,45 +61,54 @@ void mylist_resize(MyList *list, int newSize, bool runConstructor) void *mylist_add_element(MyList *list) { int oldSize; - if (list == NULL) return NULL; + + if (!list) + return NULL; + oldSize = list->size; mylist_resize(list, oldSize + 1, true); return list->data[oldSize]; } -void mylist_create(MyList **list_p, int initialCapacity, constructor_t constructor, destructor_t destructor) +void mylist_create(MyList **list_p, int initialCapacity, + constructor_t constructor, destructor_t destructor) { - MyList *list; - if (list_p == NULL) return; - if (initialCapacity < 0) initialCapacity = 0; - list = *list_p; - if (list != NULL) - { + MyList *list = NULL; + if (!list_p) + return; + if (initialCapacity < 0) + initialCapacity = 0; + list = *list_p; + if (list) mylist_destroy(list_p); - } - list = (MyList*)malloc(sizeof(MyList)); - *list_p = list; - list->size = 0; - list->Constructor = constructor; - list->Destructor = destructor; + + list = (MyList*)malloc(sizeof(MyList)); + *list_p = list; + list->size = 0; + list->Constructor = constructor; + list->Destructor = destructor; + if (initialCapacity > 0) { - list->data = (void**)malloc_zero(initialCapacity * sizeof(void*)); - list->capacity = initialCapacity; + list->data = (void**)malloc_zero(initialCapacity * sizeof(void*)); + list->capacity = initialCapacity; } else { - list->data = NULL; - list->capacity = 0; + list->data = NULL; + list->capacity = 0; } } void mylist_destroy(MyList **list_p) { - if (list_p == NULL) return; - MyList *list; + MyList *list = NULL; + if (!list_p) + return; + list = *list_p; - if (list != NULL) + + if (list) { mylist_resize(list, 0, false); free(list->data); @@ -109,11 +119,11 @@ void mylist_destroy(MyList **list_p) void mylist_assign(MyList *list, int index, void *value) { - void *oldElement; + void *oldElement = NULL; + if (index < 0 || index >= list->size) - { return; - } + oldElement = list->data[index]; list->Destructor(oldElement); list->data[index] = value; @@ -124,14 +134,13 @@ void mylist_remove_at(MyList *list, int index) int i; if (index < 0 || index >= list->size) - { return; - } + mylist_assign(list, index, NULL); + for (i = index + 1; i < list->size; i++) - { list->data[i - 1] = list->data[i]; - } + list->size--; list->data[list->size] = NULL; } @@ -144,7 +153,8 @@ void mylist_pop_front(MyList *list) void mylist_push_back(MyList *list, void *value) { int oldSize; - if (list == NULL) return; + if (!list) + return; oldSize = list->size; mylist_resize(list, oldSize + 1, false); list->data[oldSize] = value; diff --git a/runahead/mylist.h b/runahead/mylist.h index 08438378f1..cf35333938 100644 --- a/runahead/mylist.h +++ b/runahead/mylist.h @@ -1,8 +1,8 @@ #ifndef __MYLIST_H__ #define __MYLIST_H__ -#include "retro_common_api.h" -#include "boolean.h" +#include +#include RETRO_BEGIN_DECLS diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index c944079493..2c9acca268 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -62,9 +62,9 @@ static void *runahead_save_state_alloc(void) static void runahead_save_state_free(void *state) { - retro_ctx_serialize_info_t *savestate; - savestate = (retro_ctx_serialize_info_t*)state; - if (savestate == NULL) return; + retro_ctx_serialize_info_t *savestate = (retro_ctx_serialize_info_t*)state; + if (!savestate) + return; free(savestate->data); free(savestate); } @@ -72,7 +72,8 @@ static void runahead_save_state_free(void *state) static void runahead_save_state_list_init(size_t saveStateSize) { runahead_save_state_size = saveStateSize; - mylist_create(&runahead_save_state_list, 16, runahead_save_state_alloc, runahead_save_state_free); + mylist_create(&runahead_save_state_list, 16, + runahead_save_state_alloc, runahead_save_state_free); } static void runahead_save_state_list_destroy(void) @@ -107,27 +108,30 @@ static void unload_hook(void); static void add_hooks(void) { - if (originalRetroDeinit == NULL) + if (!originalRetroDeinit) { - originalRetroDeinit = current_core.retro_deinit; + originalRetroDeinit = current_core.retro_deinit; current_core.retro_deinit = deinit_hook; } - if (originalRetroUnload == NULL) + + if (!originalRetroUnload) { originalRetroUnload = current_core.retro_unload_game; current_core.retro_unload_game = unload_hook; } + add_input_state_hook(); } static void remove_hooks(void) { - if (originalRetroDeinit != NULL) + if (originalRetroDeinit) { current_core.retro_deinit = originalRetroDeinit; originalRetroDeinit = NULL; } - if (originalRetroUnload != NULL) + + if (originalRetroUnload) { current_core.retro_unload_game = originalRetroUnload; originalRetroUnload = NULL; @@ -159,33 +163,34 @@ static void unload_hook(void) /* Runahead Code */ -static bool runahead_video_driver_is_active = true; -static bool runahead_available = true; +static bool runahead_video_driver_is_active = true; +static bool runahead_available = true; static bool runahead_secondary_core_available = true; -static bool runahead_force_input_dirty = true; -static uint64_t runahead_last_frame_count = 0; +static bool runahead_force_input_dirty = true; +static uint64_t runahead_last_frame_count = 0; static void runahead_clear_variables(void) { - runahead_save_state_size = -1; - runahead_video_driver_is_active = true; - runahead_available = true; + runahead_save_state_size = -1; + runahead_video_driver_is_active = true; + runahead_available = true; runahead_secondary_core_available = true; - runahead_force_input_dirty = true; - runahead_last_frame_count = 0; + runahead_force_input_dirty = true; + runahead_last_frame_count = 0; } static void runahead_check_for_gui(void) { /* Hack: If we were in the GUI, force a resync. */ - bool is_dirty; - bool is_alive, is_focused; - uint64_t frame_count; + bool is_dirty = false; + bool is_alive, is_focused = false; + uint64_t frame_count = 0; + video_driver_get_status(&frame_count, &is_alive, &is_focused); + if (frame_count != runahead_last_frame_count + 1) - { runahead_force_input_dirty = true; - } + runahead_last_frame_count = frame_count; } @@ -207,7 +212,7 @@ void run_ahead(int runAheadCount, bool useSecondary) { if (!runahead_create()) { - /*runloop_msg_queue_push("RunAhead has been disabled because the core does not support savestates", 1, 180, true);*/ + /* RunAhead has been disabled because the core does not support savestates. */ core_run(); runahead_force_input_dirty = true; return; @@ -228,34 +233,30 @@ void run_ahead(int runAheadCount, bool useSecondary) runahead_suspend_audio(); runahead_suspend_video(); } + if (frameNumber == 0) - { core_run(); - } else - { core_run_no_input_polling(); - } + if (suspendedFrame) { runahead_resume_video(); runahead_resume_audio(); } + if (frameNumber == 0) { + /* RunAhead has been disabled due to save state failure */ if (!runahead_save_state()) - { - /*runloop_msg_queue_push("RunAhead has been disabled due to save state failure", 1, 180, true);*/ return; - } } + if (lastFrame) { + /* RunAhead has been disabled due to load state failure */ if (!runahead_load_state()) - { - /*runloop_msg_queue_push("RunAhead has been disabled due to load state failure", 1, 180, true);*/ return; - } } } } @@ -269,38 +270,40 @@ void run_ahead(int runAheadCount, bool useSecondary) if (input_is_dirty || runahead_force_input_dirty) { - input_is_dirty = false; + unsigned frame_count; + + input_is_dirty = false; + if (!runahead_save_state()) - { return; - } + + /* Could not create a secondary core. + * RunAhead wll only use the main core now. */ if (!runahead_load_state_secondary()) - { - /*runloop_msg_queue_push("Could not create a secondary core. RunAhead will only use the main core now.", 1, 180, true);*/ return; - } - for (int frameCount = 0; frameCount < runAheadCount - 1; frameCount++) + + for (frame_count = 0; frame_count < runAheadCount - 1; frame_count++) { runahead_suspend_video(); runahead_suspend_audio(); okay = runahead_run_secondary(); runahead_resume_audio(); runahead_resume_video(); + + /* Could not create a secondary core. RunAhead + * will only use the main core now. */ if (!okay) - { - /*runloop_msg_queue_push("Could not create a secondary core. RunAhead will only use the main core now.", 1, 180, true);*/ return; - } } } runahead_suspend_audio(); okay = runahead_run_secondary(); runahead_resume_audio(); + + /* Could not create a secondary core. RunAhead + * will only use the main core now. */ if (!okay) - { - /*runloop_msg_queue_push("Could not create a secondary core. RunAhead will only use the main core now.", 1, 180, true);*/ return; - } #endif } runahead_force_input_dirty = false; @@ -328,6 +331,7 @@ static bool runahead_create(void) runahead_error(); return false; } + add_hooks(); runahead_force_input_dirty = true; mylist_resize(runahead_save_state_list, 1, true); @@ -336,54 +340,45 @@ static bool runahead_create(void) static bool runahead_save_state(void) { - bool okay; - retro_ctx_serialize_info_t *serialize_info; - serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; - okay = core_serialize(serialize_info); + retro_ctx_serialize_info_t *serialize_info = + (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; + bool okay = core_serialize(serialize_info); + if (!okay) - { runahead_error(); - } return okay; } static bool runahead_load_state(void) { - bool okay; - bool lastDirty; - retro_ctx_serialize_info_t *serialize_info; - serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; - lastDirty = input_is_dirty; - okay = core_unserialize(serialize_info); + retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; + bool lastDirty = input_is_dirty; + bool okay = core_unserialize(serialize_info); input_is_dirty = lastDirty; + if (!okay) - { runahead_error(); - } + return okay; } static bool runahead_load_state_secondary(void) { - bool okay; - retro_ctx_serialize_info_t *serialize_info; - serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; - okay = secondary_core_deserialize(serialize_info->data_const, serialize_info->size); + retro_ctx_serialize_info_t *serialize_info = + (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; + bool okay = secondary_core_deserialize(serialize_info->data_const, serialize_info->size); + if (!okay) - { runahead_secondary_core_available = false; - } + return okay; } static bool runahead_run_secondary(void) { - bool okay; - okay = secondary_core_run_no_input_polling(); + bool okay = secondary_core_run_no_input_polling(); if (!okay) - { runahead_secondary_core_available = false; - } return okay; } @@ -405,13 +400,9 @@ static void runahead_suspend_video(void) static void runahead_resume_video(void) { if (runahead_video_driver_is_active) - { video_driver_set_active(); - } else - { video_driver_unset_active(); - } } void runahead_destroy(void) diff --git a/runahead/run_ahead.h b/runahead/run_ahead.h index 3f4c074d8d..77ef8b8654 100644 --- a/runahead/run_ahead.h +++ b/runahead/run_ahead.h @@ -1,8 +1,8 @@ #ifndef __RUN_AHEAD_H__ #define __RUN_AHEAD_H__ -#include "boolean.h" -#include "retro_common_api.h" +#include +#include RETRO_BEGIN_DECLS diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 58cc6ff47a..a6cf197d56 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -10,17 +10,19 @@ #endif #endif +#include +#include +#include +#include +#include +#include +#include + #include "mem_util.h" -#include "boolean.h" -#include "encodings/utf.h" -#include "compat/fopen_utf8.h" -#include "compat/unlink_utf8.h" -#include "dynamic/dylib.h" -#include "dynamic.h" -#include "core.h" -#include "file/file_path.h" -#include "paths.h" -#include "content.h" + +#include "../core.h" +#include "../paths.h" +#include "../content.h" static int port_map[16]; @@ -37,18 +39,32 @@ extern enum rarch_core_type last_core_type; extern struct retro_callbacks retro_ctx; static char* get_temp_directory_alloc(void); + static char* copy_core_to_temp_file(void); + static void* read_file_data_alloc(const char *fileName, int *size); + static bool write_file_data(const char *fileName, const void *data, int dataSize); -static bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, int dataSize); + +static bool write_file_with_random_name(char **tempDllPath, + const char *retroarchTempPath, const void* data, int dataSize); + static void* InputListElementConstructor(void); + static void secondary_core_clear(void); + static bool secondary_core_create(void); + bool secondary_core_run_no_input_polling(void); + bool secondary_core_deserialize(const void *buffer, int size); + void secondary_core_destroy(void); + void set_last_core_type(enum rarch_core_type type); + void remember_controller_port_device(long port, long device); + void clear_controller_port_map(void); static void free_file(FILE **file_p); @@ -88,28 +104,21 @@ char* get_temp_directory_alloc(void) char* copy_core_to_temp_file(void) { - bool okay; - const char *corePath = NULL; /* ptr to static buffer, do not need to free this */ - const char *coreBaseName = NULL; /* ptr to static buffer, do not need to free this */ - char *tempDirectory = NULL; - char *retroarchTempPath = NULL; - char *tempDllPath = NULL; - void *dllFileData = NULL; - int dllFileSize = 0; - - corePath = path_get(RARCH_PATH_CORE); - coreBaseName = path_basename(corePath); + bool okay = false; + char *tempDirectory = NULL; + char *retroarchTempPath = NULL; + char *tempDllPath = NULL; + void *dllFileData = NULL; + int dllFileSize = 0; + const char *corePath = path_get(RARCH_PATH_CORE); + const char *coreBaseName = path_basename(corePath); if (strlen(coreBaseName) == 0) - { goto failed; - } tempDirectory = get_temp_directory_alloc(); - if (tempDirectory == NULL) - { + if (!tempDirectory) goto failed; - } strcat_alloc(&retroarchTempPath, tempDirectory); strcat_alloc(&retroarchTempPath, path_default_slash()); @@ -117,27 +126,25 @@ char* copy_core_to_temp_file(void) strcat_alloc(&retroarchTempPath, path_default_slash()); okay = path_mkdir(retroarchTempPath); + if (!okay) - { goto failed; - } dllFileData = read_file_data_alloc(corePath, &dllFileSize); - if (dllFileData == NULL) - { + + if (!dllFileData) goto failed; - } + strcat_alloc(&tempDllPath, retroarchTempPath); strcat_alloc(&tempDllPath, coreBaseName); okay = write_file_data(tempDllPath, dllFileData, dllFileSize); + if (!okay) { /* try other file names */ okay = write_file_with_random_name(&tempDllPath, retroarchTempPath, dllFileData, dllFileSize); if (!okay) - { goto failed; - } } success: free_str(&tempDirectory); @@ -155,20 +162,19 @@ failed: void* read_file_data_alloc(const char *fileName, int *size) { - void *data = NULL; - FILE *f = NULL; - int fileSize = 0; - size_t bytesRead = 0; + void *data = NULL; + int fileSize = 0; + size_t bytesRead = 0; #ifdef _WIN32 int64_t fileSizeLong = 0; #else off64_t fileSizeLong = 0; #endif - f = (FILE*)fopen_utf8(fileName, "rb"); - if (f == NULL) - { + FILE *f = (FILE*)fopen_utf8(fileName, "rb"); + + if (!f) goto failed; - } + fseek(f, 0, SEEK_END); #ifdef _WIN32 fileSizeLong = _ftelli64(f); @@ -176,46 +182,48 @@ void* read_file_data_alloc(const char *fileName, int *size) fileSizeLong = ftello64(f); #endif fseek(f, 0, SEEK_SET); + /* 256MB file size limit for DLL files */ if (fileSizeLong < 0 || fileSizeLong > 256 * 1024 * 1024) - { goto failed; - } + fileSize = (int)fileSizeLong; - data = malloc(fileSize); - if (data == NULL) - { + data = malloc(fileSize); + + if (!data) goto failed; - } + bytesRead = fread(data, 1, fileSize, f); + if ((int)bytesRead != (int)fileSize) - { goto failed; - } + success: free_file(&f); - if (size != NULL) *size = fileSize; + if (size) + *size = fileSize; return data; failed: free_ptr(&data); free_file(&f); - if (size != NULL) *size = 0; + if (size) + *size = 0; return NULL; } bool write_file_data(const char *fileName, const void *data, int dataSize) { - bool okay = false; - FILE *f = NULL; + bool okay = false; size_t bytesWritten = 0; + FILE *f = (FILE*)fopen_utf8(fileName, "wb"); - f = (FILE*)fopen_utf8(fileName, "wb"); - if (f == NULL) goto failed; - bytesWritten = fwrite(data, 1, dataSize, f); - if (bytesWritten != dataSize) - { + if (!f) goto failed; - } + bytesWritten = fwrite(data, 1, dataSize, f); + + if (bytesWritten != dataSize) + goto failed; + success: free_file(&f); return true; @@ -224,21 +232,20 @@ failed: return false; } -bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, int dataSize) +bool write_file_with_random_name(char **tempDllPath, + const char *retroarchTempPath, const void* data, int dataSize) { - int extLen; - char *ext = NULL; - bool okay = false; - const int maxAttempts = 30; - const char *prefix = "tmp"; - char numberBuf[32]; - time_t timeValue = time(NULL); - unsigned int numberValue = (unsigned int)timeValue; - int number = 0; int i; + char numberBuf[32]; + bool okay = false; + const int maxAttempts = 30; + const char *prefix = "tmp"; + time_t timeValue = time(NULL); + unsigned int numberValue = (unsigned int)timeValue; + int number = 0; + char *ext = strcpy_alloc_force(path_get_extension(*tempDllPath)); + int extLen = strlen(ext); - ext = strcpy_alloc_force(path_get_extension(*tempDllPath)); - extLen = strlen(ext); if (extLen > 0) { strcat_alloc(&ext, "."); @@ -247,7 +254,6 @@ bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPa extLen++; } - /* try up to 30 'random' filenames before giving up */ for (i = 0; i < 30; i++) { @@ -261,9 +267,7 @@ bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPa strcat_alloc(tempDllPath, ext); okay = write_file_data(*tempDllPath, data, dataSize); if (okay) - { break; - } } success: free_str(&ext); @@ -276,7 +280,7 @@ failed: void secondary_core_clear(void) { secondary_library_path = NULL; - secondary_module = NULL; + secondary_module = NULL; memset(&secondary_core, 0, sizeof(struct retro_core_t)); } @@ -285,17 +289,17 @@ bool secondary_core_create(void) long port, device; bool contentless, is_inited; - if (last_core_type != CORE_TYPE_PLAIN || load_content_info == NULL || load_content_info->special != NULL) - { + if ( last_core_type != CORE_TYPE_PLAIN || + !load_content_info || + load_content_info->special) return false; - } free_str(&secondary_library_path); secondary_library_path = copy_core_to_temp_file(); - if (secondary_library_path == NULL) - { + + if (!secondary_library_path) return false; - } + /* Load Core */ if (init_libretro_sym_custom(CORE_TYPE_PLAIN, &secondary_core, secondary_library_path, &secondary_module)) { @@ -315,12 +319,13 @@ bool secondary_core_create(void) secondary_core.inited = is_inited; /* Load Content */ - if (load_content_info == NULL || load_content_info->special != NULL) + if (!load_content_info || load_content_info->special) { /* disabled due to crashes */ return false; #if 0 - secondary_core.game_loaded = secondary_core.retro_load_game_special(loadContentInfo.special->id, loadContentInfo.info, loadContentInfo.content->size); + secondary_core.game_loaded = secondary_core.retro_load_game_special( + loadContentInfo.special->id, loadContentInfo.info, loadContentInfo.content->size); if (!secondary_core.game_loaded) { secondary_core_destroy(); @@ -328,7 +333,7 @@ bool secondary_core_create(void) } #endif } - else if (load_content_info->content->size > 0 && load_content_info->content->elems[0].data != NULL) + else if (load_content_info->content->size > 0 && load_content_info->content->elems[0].data) { secondary_core.game_loaded = secondary_core.retro_load_game(load_content_info->info); if (!secondary_core.game_loaded) @@ -360,29 +365,23 @@ bool secondary_core_create(void) { device = port_map[port]; if (device >= 0) - { secondary_core.retro_set_controller_port_device(port, device); - } } clear_controller_port_map(); } else - { return false; - } + return true; } bool secondary_core_run_no_input_polling(void) { - bool okay; - if (secondary_module == NULL) + if (!secondary_module) { - okay = secondary_core_create(); + bool okay = secondary_core_create(); if (!okay) - { return false; - } } secondary_core.retro_run(); return true; @@ -390,10 +389,9 @@ bool secondary_core_run_no_input_polling(void) bool secondary_core_deserialize(const void *buffer, int size) { - bool okay; - if (secondary_module == NULL) + if (!secondary_module) { - okay = secondary_core_create(); + bool okay = secondary_core_create(); if (!okay) { secondary_core_destroy(); @@ -405,18 +403,14 @@ bool secondary_core_deserialize(const void *buffer, int size) void secondary_core_destroy(void) { - if (secondary_module != NULL) + if (secondary_module) { /* unload game from core */ - if (secondary_core.retro_unload_game != NULL) - { + if (secondary_core.retro_unload_game) secondary_core.retro_unload_game(); - } /* deinit */ - if (secondary_core.retro_deinit != NULL) - { + if (secondary_core.retro_deinit) secondary_core.retro_deinit(); - } memset(&secondary_core, 0, sizeof(struct retro_core_t)); dylib_close(secondary_module); @@ -429,44 +423,34 @@ void secondary_core_destroy(void) void remember_controller_port_device(long port, long device) { if (port >= 0 && port < 16) - { port_map[port] = device; - } - if (secondary_module != NULL && secondary_core.retro_set_controller_port_device != NULL) - { + if (secondary_module && secondary_core.retro_set_controller_port_device) secondary_core.retro_set_controller_port_device(port, device); - } } void clear_controller_port_map(void) { - int port; + unsigned port; for (port = 0; port < 16; port++) - { port_map[port] = -1; - } } static void free_file(FILE **file_p) { - bool result; - if (file_p == NULL) - { + bool result = false; + if (!file_p || !*file_p) return; - } - if (*file_p == NULL) - { - return; - } - result = fclose(*file_p) != 0; + result = fclose(*file_p) != 0; *file_p = NULL; return; } #else -#include "boolean.h" -#include "core.h" +#include + +#include "../core.h" + bool secondary_core_run_no_input_polling(void) { return false; diff --git a/runahead/secondary_core.h b/runahead/secondary_core.h index 5393c1f6e0..b135aee81b 100644 --- a/runahead/secondary_core.h +++ b/runahead/secondary_core.h @@ -1,18 +1,19 @@ #ifndef __SECONDARY_CORE_H__ #define __SECONDARY_CORE_H__ -#include "core_type.h" -#include "retro_common_api.h" -#include "boolean.h" +#include +#include + +#include "../core_type.h" RETRO_BEGIN_DECLS -bool secondary_core_run_no_input_polling(); +bool secondary_core_run_no_input_polling(void); bool secondary_core_deserialize(const void *buffer, int size); -void secondary_core_destroy(); +void secondary_core_destroy(void); void set_last_core_type(enum rarch_core_type type); void remember_controller_port_device(long port, long device); -void clear_controller_port_map(); +void clear_controller_port_map(void); RETRO_END_DECLS From 1b0420807bfb4600d6a06c1a848af90fc8e54457 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:15:47 +0200 Subject: [PATCH 029/517] (Griffin/MSVC) buildfixes --- core_impl.c | 4 ++-- libretro-common/compat/unlink_utf8.c | 2 +- runahead/run_ahead.c | 13 ++++++++----- runahead/secondary_core.c | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/core_impl.c b/core_impl.c index 245f1bc31f..ce33e8e4d0 100644 --- a/core_impl.c +++ b/core_impl.c @@ -283,11 +283,11 @@ bool core_get_memory(retro_ctx_memory_info_t *info) bool core_load_game(retro_ctx_load_content_info_t *load_info) { - set_load_content_info(load_info); - bool contentless = false; bool is_inited = false; + set_load_content_info(load_info); + content_get_status(&contentless, &is_inited); if (load_info && load_info->special) diff --git a/libretro-common/compat/unlink_utf8.c b/libretro-common/compat/unlink_utf8.c index ce3bcad01e..8ca47c7790 100644 --- a/libretro-common/compat/unlink_utf8.c +++ b/libretro-common/compat/unlink_utf8.c @@ -18,7 +18,7 @@ bool unlink_utf8(const char * filename) { #if defined(LEGACY_WIN32) - bool result = DeleteFileA(filename_w); + bool result = DeleteFileA(filename); #else wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); bool result = DeleteFileW(filename_w); diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 2c9acca268..bf79fe1fa9 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -1,15 +1,18 @@ -#include "core.h" -#include "dynamic.h" -#include "audio/audio_driver.h" -#include "gfx/video_driver.h" -#include "boolean.h" #include #include #include + +#include + #include "dirty_input.h" #include "mylist.h" #include "secondary_core.h" +#include "../core.h" +#include "../dynamic.h" +#include "../audio/audio_driver.h" +#include "../gfx/video_driver.h" + static void *runahead_save_state_alloc(void); static void runahead_save_state_free(void *state); static void runahead_save_state_list_init(size_t saveStateSize); diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index a6cf197d56..845299319c 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -15,12 +15,12 @@ #include #include #include -#include #include #include "mem_util.h" #include "../core.h" +#include "../dynamic.h" #include "../paths.h" #include "../content.h" From 4372db491aa96fb11a76f360c870d87058fadb63 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:20:43 +0200 Subject: [PATCH 030/517] Cleanup some warnings for C89_BUILD --- dynamic.c | 18 ++++++++++-------- retroarch.c | 4 ---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/dynamic.c b/dynamic.c index 86c7996cb4..22d4476900 100644 --- a/dynamic.c +++ b/dynamic.c @@ -914,18 +914,20 @@ static bool dynamic_request_hw_context(enum retro_hw_context_type type, case RETRO_HW_CONTEXT_DIRECT3D: switch(major) - { + { #ifdef HAVE_D3D9 - case 9: + case 9: + RARCH_LOG("Requesting D3D9 context.\n"); + break; #endif #ifdef HAVE_D3D11 - case 11: + case 11: + RARCH_LOG("Requesting D3D11 context.\n"); + break; #endif - RARCH_LOG("Requesting D3D%i context.\n", major); - break; - default: - goto unknown; - } + default: + goto unknown; + } break; unknown: diff --git a/retroarch.c b/retroarch.c index ca6187a52c..a15ab8e714 100644 --- a/retroarch.c +++ b/retroarch.c @@ -3252,13 +3252,9 @@ int runloop_iterate(unsigned *sleep_ms) /* Run Ahead Feature replaces the call to core_run in this loop */ if (settings->bools.run_ahead_enabled && settings->uints.run_ahead_frames > 0) - { run_ahead(settings->uints.run_ahead_frames, settings->bools.run_ahead_secondary_instance); - } else - { core_run(); - } #ifdef HAVE_CHEEVOS if (runloop_check_cheevos()) From ef5830b2331769a3333aaf19edacc6e5b315298c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:27:30 +0200 Subject: [PATCH 031/517] Silence some CXX_BUILD warnings --- gfx/common/d3d12_common.c | 6 +++--- gfx/drivers/d3d12.c | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/gfx/common/d3d12_common.c b/gfx/common/d3d12_common.c index 0614c84439..b6143c4cf9 100644 --- a/gfx/common/d3d12_common.c +++ b/gfx/common/d3d12_common.c @@ -372,18 +372,18 @@ bool d3d12_init_descriptors(d3d12_video_t* d3d12) cs_root_params[CS_ROOT_ID_TEXTURE_T].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; cs_root_params[CS_ROOT_ID_TEXTURE_T].DescriptorTable.NumDescriptorRanges = countof(srv_tbl); cs_root_params[CS_ROOT_ID_TEXTURE_T].DescriptorTable.pDescriptorRanges = srv_tbl; - cs_root_params[CS_ROOT_ID_TEXTURE_T].ShaderVisibility = 0; + cs_root_params[CS_ROOT_ID_TEXTURE_T].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; cs_root_params[CS_ROOT_ID_UAV_T].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; cs_root_params[CS_ROOT_ID_UAV_T].DescriptorTable.NumDescriptorRanges = countof(uav_tbl); cs_root_params[CS_ROOT_ID_UAV_T].DescriptorTable.pDescriptorRanges = uav_tbl; - cs_root_params[CS_ROOT_ID_UAV_T].ShaderVisibility = 0; + cs_root_params[CS_ROOT_ID_UAV_T].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; cs_root_params[CS_ROOT_ID_CONSTANTS].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; cs_root_params[CS_ROOT_ID_CONSTANTS].Constants.Num32BitValues = 3; cs_root_params[CS_ROOT_ID_CONSTANTS].Constants.RegisterSpace = 0; cs_root_params[CS_ROOT_ID_CONSTANTS].Constants.ShaderRegister = 0; - cs_root_params[CS_ROOT_ID_CONSTANTS].ShaderVisibility = 0; + cs_root_params[CS_ROOT_ID_CONSTANTS].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; static_sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; static_sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 2b942fc868..85a53a20f8 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -1298,19 +1298,20 @@ static bool d3d12_gfx_frame( while (texture_sem->stage_mask) { { - D3D12_CPU_DESCRIPTOR_HANDLE handle = { + D3D12_CPU_DESCRIPTOR_HANDLE handle = { d3d12->pass[i].textures.ptr - d3d12->desc.srv_heap.gpu.ptr + - d3d12->desc.srv_heap.cpu.ptr + - texture_sem->binding * d3d12->desc.srv_heap.stride + d3d12->desc.srv_heap.cpu.ptr + + texture_sem->binding * d3d12->desc.srv_heap.stride }; - d3d12_texture_t* tex = texture_sem->texture_data; + d3d12_texture_t* tex = (d3d12_texture_t*)texture_sem->texture_data; D3D12_SHADER_RESOURCE_VIEW_DESC desc = { tex->desc.Format }; - desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - desc.Texture2D.MipLevels = tex->desc.MipLevels; + desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + desc.Texture2D.MipLevels = tex->desc.MipLevels; - D3D12CreateShaderResourceView(d3d12->device, tex->handle, &desc, handle); + D3D12CreateShaderResourceView(d3d12->device, + tex->handle, &desc, handle); } { @@ -1669,7 +1670,7 @@ static void d3d12_gfx_set_osd_msg( if (d3d12) { if (d3d12->sprites.enabled) - font_driver_render_msg(video_info, font, msg, params); + font_driver_render_msg(video_info, font, msg, (const font_params*)params); else printf("OSD msg: %s\n", msg); } From 6e662a8064612c9cdf8688c2c353a7c55cc888d4 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:29:36 +0200 Subject: [PATCH 032/517] (D3D12) Buildfix --- gfx/drivers/d3d12.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 85a53a20f8..dafe301cea 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -1670,7 +1670,7 @@ static void d3d12_gfx_set_osd_msg( if (d3d12) { if (d3d12->sprites.enabled) - font_driver_render_msg(video_info, font, msg, (const font_params*)params); + font_driver_render_msg(video_info, font, msg, (const struct font_params*)params); else printf("OSD msg: %s\n", msg); } From c041470bb09278ed4608f24ef0aeb7bcbcb12a00 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:38:22 +0200 Subject: [PATCH 033/517] Add HAVE_RUNAHEAD ifdef --- Makefile.common | 6 +++++- core_impl.c | 6 ++++++ dynamic.c | 23 ++++++++++++++++------- griffin/griffin.c | 2 ++ qb/config.params.sh | 1 + retroarch.c | 4 ++++ 6 files changed, 34 insertions(+), 8 deletions(-) diff --git a/Makefile.common b/Makefile.common index ae667704fa..972d61e34e 100644 --- a/Makefile.common +++ b/Makefile.common @@ -259,12 +259,16 @@ OBJ += frontend/frontend.o \ $(LIBRETRO_COMM_DIR)/features/features_cpu.o \ performance_counters.o \ verbosity.o \ - runahead/copy_load_info.o \ + +ifeq ($(HAVE_RUNAHEAD), 1) +DEFINES += -DHAVE_RUNAHEAD +OBJ += runahead/copy_load_info.o \ runahead/dirty_input.o \ runahead/mem_util.o \ runahead/mylist.o \ runahead/run_ahead.o \ runahead/secondary_core.o +endif diff --git a/core_impl.c b/core_impl.c index ce33e8e4d0..5acc659943 100644 --- a/core_impl.c +++ b/core_impl.c @@ -44,8 +44,10 @@ #include "gfx/video_driver.h" #include "audio/audio_driver.h" +#ifdef HAVE_RUNAHEAD #include "runahead/copy_load_info.h" #include "runahead/secondary_core.h" +#endif struct retro_callbacks retro_ctx; struct retro_core_t current_core; @@ -266,7 +268,9 @@ bool core_set_controller_port_device(retro_ctx_controller_info_t *pad) if (!pad) return false; +#ifdef HAVE_RUNAHEAD remember_controller_port_device(pad->port, pad->device); +#endif current_core.retro_set_controller_port_device(pad->port, pad->device); return true; @@ -286,7 +290,9 @@ bool core_load_game(retro_ctx_load_content_info_t *load_info) bool contentless = false; bool is_inited = false; +#ifdef HAVE_RUNAHEAD set_load_content_info(load_info); +#endif content_get_status(&contentless, &is_inited); diff --git a/dynamic.c b/dynamic.c index 22d4476900..e312e03cc6 100644 --- a/dynamic.c +++ b/dynamic.c @@ -66,7 +66,9 @@ #include "msg_hash.h" #include "verbosity.h" +#ifdef HAVE_RUNAHEAD #include "runahead/secondary_core.h" +#endif #ifdef HAVE_DYNAMIC #define SYMBOL(x) do { \ @@ -393,24 +395,28 @@ bool init_libretro_sym_custom(enum rarch_core_type type, struct retro_core_t *cu { case CORE_TYPE_PLAIN: #ifdef HAVE_DYNAMIC - - if (lib_path == NULL || lib_handle_p == NULL) +#ifdef HAVE_RUNAHEAD + if (!lib_path || !lib_handle_p) +#endif { if (!load_dynamic_core()) return false; lib_handle_local = lib_handle; } +#ifdef HAVE_RUNAHEAD else { - /* for a secondary core, we already have a primary library loaded, so we can skip some checks and just load the library */ + /* for a secondary core, we already have a + * primary library loaded, so we can skip + * some checks and just load the library */ retro_assert(lib_path != NULL && lib_handle_p != NULL); lib_handle_local = dylib_load(lib_path); - if (lib_handle_local == NULL) - { + + if (!lib_handle_local) return false; - } *lib_handle_p = lib_handle_local; } +#endif #endif SYMBOL(retro_init); @@ -659,8 +665,11 @@ bool init_libretro_sym(enum rarch_core_type type, struct retro_core_t *current_c if (!load_symbols(type, current_core)) return false; - /* remember last core type created, so creating a secondary core will know what core type to use */ +#ifdef HAVE_RUNAHEAD + /* remember last core type created, so creating a + * secondary core will know what core type to use. */ set_last_core_type(type); +#endif return true; } diff --git a/griffin/griffin.c b/griffin/griffin.c index 40b0f89ac7..5cf227c954 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1255,12 +1255,14 @@ MENU #include "../libretro-common/net/net_http_parse.c" #endif +#ifdef HAVE_RUNAHEAD #include "../runahead/mem_util.c" #include "../runahead/secondary_core.c" #include "../runahead/run_ahead.c" #include "../runahead/copy_load_info.c" #include "../runahead/dirty_input.c" #include "../runahead/mylist.c" +#endif /*============================================================ DEPENDENCIES diff --git a/qb/config.params.sh b/qb/config.params.sh index a1be97d869..94135f94f6 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -11,6 +11,7 @@ HAVE_MATERIALUI=auto # MaterialUI menu HAVE_XMB=auto # XMB menu HAVE_ZARCH=no # Zarch menu HAVE_NUKLEAR=no # Nuklear menu +HAVE_RUNAHEAD=yes # Runahead support HAVE_OVERLAY=yes # Overlay support HAVE_DYNAMIC=yes # Dynamic loading of libretro library HAVE_SDL=auto # SDL support diff --git a/retroarch.c b/retroarch.c index a15ab8e714..eee794302a 100644 --- a/retroarch.c +++ b/retroarch.c @@ -118,7 +118,9 @@ #include "command.h" +#ifdef HAVE_RUNAHEAD #include "runahead/run_ahead.h" +#endif #define _PSUPP(var, name, desc) printf(" %s:\n\t\t%s: %s\n", name, desc, _##var##_supp ? "yes" : "no") @@ -3250,10 +3252,12 @@ int runloop_iterate(unsigned *sleep_ms) if ((settings->uints.video_frame_delay > 0) && !input_nonblock_state) retro_sleep(settings->uints.video_frame_delay); +#ifdef HAVE_RUNAHEAD /* Run Ahead Feature replaces the call to core_run in this loop */ if (settings->bools.run_ahead_enabled && settings->uints.run_ahead_frames > 0) run_ahead(settings->uints.run_ahead_frames, settings->bools.run_ahead_secondary_instance); else +#endif core_run(); #ifdef HAVE_CHEEVOS From a84facf636b23762919c617d0c344a0d85d14a75 Mon Sep 17 00:00:00 2001 From: alfrix Date: Sun, 25 Mar 2018 12:27:17 -0300 Subject: [PATCH 034/517] Add Left Thumbnails Fix behaviour when right thumb is off and use as fallback in filebrowser Remove update_left_thumbnail_path Fix dual image in imageviewer Remove xmb_left_thumbnails_ident Requested Changes Fix last warning and button not switching thumb Better scaling Limit vertical size without changing position Change thumb cycling behaviour Remove update_left_thumbnail_image it was causing problems --- CHANGES.md | 1 + config.def.h | 2 + configuration.c | 22 ++-- configuration.h | 1 + intl/msg_hash_lbl.h | 2 + intl/msg_hash_us.h | 6 + menu/cbs/menu_cbs_get_value.c | 45 +++++++ menu/cbs/menu_cbs_scan.c | 24 ++-- menu/cbs/menu_cbs_sublabel.c | 8 +- menu/drivers/materialui.c | 5 +- menu/drivers/xmb.c | 231 ++++++++++++++++++++++++++++------ menu/menu_displaylist.c | 23 ++-- menu/menu_driver.c | 27 +++- menu/menu_driver.h | 6 +- menu/menu_setting.c | 29 +++-- msg_hash.h | 4 +- retroarch.cfg | 1 + 17 files changed, 352 insertions(+), 85 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4fd33cccb3..fbc6cd11a9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ - LOCALIZATION: Update Italian translation. - LOCALIZATION: Update Polish translation. - MENU: Disable XMB shadow icons by default for PowerPC and ARM for performance reasons. +- MENU/XMB: Add Left Thumbnails (additional to the right). - MENU/XMB: Fixed left/right tab regression. - MENU/XMB: Fix scaling of tall images that were cut on bottom previously. - MENU/XMB: Menu scale factor setting now changes texts length, image scaling and margins. diff --git a/config.def.h b/config.def.h index 98430898f1..6223deddcf 100644 --- a/config.def.h +++ b/config.def.h @@ -652,6 +652,8 @@ static const unsigned input_bind_timeout = 5; static const unsigned menu_thumbnails_default = 3; +static const unsigned menu_left_thumbnails_default = 0; + #ifdef IOS static const bool ui_companion_start_on_boot = false; #else diff --git a/configuration.c b/configuration.c index 2e259ebb12..78fcea8d6c 100644 --- a/configuration.c +++ b/configuration.c @@ -4,6 +4,7 @@ * Copyright (C) 2014-2017 - Jean-André Santoni * Copyright (C) 2015-2017 - Andrés Suárez * Copyright (C) 2016-2017 - Brad Parker + * Copyright (C) 2018 - Alfredo Monclús * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- @@ -1474,6 +1475,7 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("dpi_override_value", &settings->uints.menu_dpi_override_value, true, menu_dpi_override_value, false); SETTING_UINT("menu_thumbnails", &settings->uints.menu_thumbnails, true, menu_thumbnails_default, false); #ifdef HAVE_XMB + SETTING_UINT("menu_left_thumbnails", &settings->uints.menu_left_thumbnails, true, menu_left_thumbnails_default, false); SETTING_UINT("xmb_alpha_factor", &settings->uints.menu_xmb_alpha_factor, true, xmb_alpha_factor, false); SETTING_UINT("xmb_scale_factor", &settings->uints.menu_xmb_scale_factor, true, xmb_scale_factor, false); SETTING_UINT("xmb_theme", &settings->uints.menu_xmb_theme, true, xmb_icon_theme, false); @@ -2930,7 +2932,7 @@ bool config_load_override(void) config_file_t *new_conf = NULL; bool should_append = false; rarch_system_info_t *system = runloop_get_system_info(); - const char *core_name = system ? + const char *core_name = system ? system->info.library_name : NULL; const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); @@ -3018,7 +3020,7 @@ bool config_load_override(void) if (!should_append) goto error; - /* Re-load the configuration with any overrides + /* Re-load the configuration with any overrides * that might have been found */ buf[0] = '\0'; @@ -3103,7 +3105,7 @@ bool config_unload_override(void) bool config_load_remap(void) { size_t path_size = PATH_MAX_LENGTH * sizeof(char); - char *remap_directory = NULL; + char *remap_directory = NULL; char *core_path = NULL; char *game_path = NULL; config_file_t *new_conf = NULL; @@ -3123,12 +3125,12 @@ bool config_load_remap(void) /* path to the directory containing retroarch.cfg (prefix) */ remap_directory = (char*) malloc(PATH_MAX_LENGTH * sizeof(char)); - /* final path for core-specific configuration (prefix+suffix) */ + /* final path for core-specific configuration (prefix+suffix) */ core_path = (char*) malloc(PATH_MAX_LENGTH * sizeof(char)); /* final path for game-specific configuration (prefix+suffix) */ game_path = (char*) - malloc(PATH_MAX_LENGTH * sizeof(char)); + malloc(PATH_MAX_LENGTH * sizeof(char)); remap_directory[0] = core_path[0] = game_path[0] = '\0'; strlcpy(remap_directory, @@ -3230,7 +3232,7 @@ bool config_load_shader_preset(void) char *parent_path = NULL; settings_t *settings = config_get_ptr(); rarch_system_info_t *system = runloop_get_system_info(); - const char *core_name = system + const char *core_name = system ? system->info.library_name : NULL; const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); @@ -4181,28 +4183,28 @@ bool config_save_overrides(int override_type) char cfg[64]; cfg[0] = '\0'; - if (settings->uints.input_device[i] + if (settings->uints.input_device[i] != overrides->uints.input_device[i]) { snprintf(cfg, sizeof(cfg), "input_device_p%u", i + 1); config_set_int(conf, cfg, overrides->uints.input_device[i]); } - if (settings->uints.input_joypad_map[i] + if (settings->uints.input_joypad_map[i] != overrides->uints.input_joypad_map[i]) { snprintf(cfg, sizeof(cfg), "input_player%u_joypad_index", i + 1); config_set_int(conf, cfg, overrides->uints.input_joypad_map[i]); } - if (settings->uints.input_libretro_device[i] + if (settings->uints.input_libretro_device[i] != overrides->uints.input_libretro_device[i]) { snprintf(cfg, sizeof(cfg), "input_libretro_device_p%u", i + 1); config_set_int(conf, cfg, overrides->uints.input_libretro_device[i]); } - if (settings->uints.input_analog_dpad_mode[i] + if (settings->uints.input_analog_dpad_mode[i] != overrides->uints.input_analog_dpad_mode[i]) { snprintf(cfg, sizeof(cfg), "input_player%u_analog_dpad_mode", i + 1); diff --git a/configuration.h b/configuration.h index 02b646cb0a..61909d8a95 100644 --- a/configuration.h +++ b/configuration.h @@ -350,6 +350,7 @@ typedef struct settings unsigned video_msg_bgcolor_blue; unsigned menu_thumbnails; + unsigned menu_left_thumbnails; unsigned menu_dpi_override_value; unsigned menu_entry_normal_color; unsigned menu_entry_hover_color; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 4152488f14..88ce40ea04 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1035,6 +1035,8 @@ MSG_HASH(MENU_ENUM_LABEL_THREADED_DATA_RUNLOOP_ENABLE, "threaded_data_runloop_enable") MSG_HASH(MENU_ENUM_LABEL_THUMBNAILS, "thumbnails") +MSG_HASH(MENU_ENUM_LABEL_LEFT_THUMBNAILS, + "left thumbnails") MSG_HASH(MENU_ENUM_LABEL_THUMBNAILS_DIRECTORY, "thumbnails_directory") MSG_HASH(MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 97584df271..9ac3a8728a 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1631,6 +1631,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_THREADED_DATA_RUNLOOP_ENABLE, "Threaded tasks") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS, "Thumbnails") +MSG_HASH(MENU_ENUM_LABEL_VALUE_LEFT_THUMBNAILS, + "Left Thumbnails") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS_DIRECTORY, "Thumbnails") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS_UPDATER_LIST, @@ -2926,6 +2928,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_THUMBNAILS, "Type of thumbnail to display." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_LEFT_THUMBNAILS, + "Type of thumbnail to display at the left." + ) MSG_HASH( MENU_ENUM_SUBLABEL_TIMEDATE_ENABLE, "Shows current date and/or time inside the menu." diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index ec8c1b4005..a3679e66c0 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -1134,6 +1134,47 @@ static void menu_action_setting_disp_set_label_thumbnails( } } +static void menu_action_setting_disp_set_label_left_thumbnails( + file_list_t* list, + unsigned *w, unsigned type, unsigned i, + const char *label, + char *s, size_t len, + const char *entry_label, + const char *path, + char *s2, size_t len2) +{ + settings_t *settings = config_get_ptr(); + + if (!settings) + return; + + strlcpy(s2, path, len2); + *w = 19; + + switch (settings->uints.menu_left_thumbnails) + { + case 0: + strlcpy(s, msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_OFF), len); + break; + case 1: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_SCREENSHOTS), len); + break; + case 2: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_TITLE_SCREENS), len); + break; + case 3: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_BOXARTS), len); + break; + } +} + static void menu_action_setting_disp_set_label_menu_toggle_gamepad_combo( file_list_t* list, unsigned *w, unsigned type, unsigned i, @@ -1912,6 +1953,10 @@ static int menu_cbs_init_bind_get_string_representation_compare_label( BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_thumbnails); break; + case MENU_ENUM_LABEL_LEFT_THUMBNAILS: + BIND_ACTION_GET_VALUE(cbs, + menu_action_setting_disp_set_label_left_thumbnails); + break; case MENU_ENUM_LABEL_INPUT_MENU_ENUM_TOGGLE_GAMEPAD_COMBO: BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_menu_toggle_gamepad_combo); diff --git a/menu/cbs/menu_cbs_scan.c b/menu/cbs/menu_cbs_scan.c index ed7fc081c1..c878491f29 100644 --- a/menu/cbs/menu_cbs_scan.c +++ b/menu/cbs/menu_cbs_scan.c @@ -104,14 +104,22 @@ int action_switch_thumbnail(const char *path, if (!settings) return -1; - settings->uints.menu_thumbnails++; - - if (settings->uints.menu_thumbnails > 3) - settings->uints.menu_thumbnails = 0; - - menu_driver_ctl(RARCH_MENU_CTL_UPDATE_THUMBNAIL_PATH, NULL); - menu_driver_ctl(RARCH_MENU_CTL_UPDATE_THUMBNAIL_IMAGE, NULL); - + if (settings->uints.menu_thumbnails == 0) + { + settings->uints.menu_left_thumbnails++; + if (settings->uints.menu_left_thumbnails > 3) + settings->uints.menu_left_thumbnails = 1; + menu_driver_ctl(RARCH_MENU_CTL_UPDATE_THUMBNAIL_PATH, NULL); + menu_driver_ctl(RARCH_MENU_CTL_UPDATE_THUMBNAIL_IMAGE, NULL); + } + else + { + settings->uints.menu_thumbnails++; + if (settings->uints.menu_thumbnails > 3) + settings->uints.menu_thumbnails = 1; + menu_driver_ctl(RARCH_MENU_CTL_UPDATE_THUMBNAIL_PATH, NULL); + menu_driver_ctl(RARCH_MENU_CTL_UPDATE_THUMBNAIL_IMAGE, NULL); + } return 0; } diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 319ee1f875..9db59de319 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -225,6 +225,7 @@ default_sublabel_macro(action_bind_sublabel_stdin_cmd_enable, MENU_ default_sublabel_macro(action_bind_sublabel_mouse_enable, MENU_ENUM_SUBLABEL_MOUSE_ENABLE) default_sublabel_macro(action_bind_sublabel_pointer_enable, MENU_ENUM_SUBLABEL_POINTER_ENABLE) default_sublabel_macro(action_bind_sublabel_thumbnails, MENU_ENUM_SUBLABEL_THUMBNAILS) +default_sublabel_macro(action_bind_sublabel_left_thumbnails, MENU_ENUM_SUBLABEL_LEFT_THUMBNAILS) default_sublabel_macro(action_bind_sublabel_timedate_enable, MENU_ENUM_SUBLABEL_TIMEDATE_ENABLE) default_sublabel_macro(action_bind_sublabel_battery_level_enable, MENU_ENUM_SUBLABEL_BATTERY_LEVEL_ENABLE) default_sublabel_macro(action_bind_sublabel_navigation_wraparound, MENU_ENUM_SUBLABEL_NAVIGATION_WRAPAROUND) @@ -416,8 +417,8 @@ static int action_bind_sublabel_subsystem_add( if (subsystem && content_get_subsystem_rom_id() < subsystem->num_roms) snprintf(s, len, " Current Content: %s", - content_get_subsystem() == type - MENU_SETTINGS_SUBSYSTEM_ADD - ? subsystem->roms[content_get_subsystem_rom_id()].desc + content_get_subsystem() == type - MENU_SETTINGS_SUBSYSTEM_ADD + ? subsystem->roms[content_get_subsystem_rom_id()].desc : subsystem->roms[0].desc); return 0; @@ -962,6 +963,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_THUMBNAILS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_thumbnails); break; + case MENU_ENUM_LABEL_LEFT_THUMBNAILS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_left_thumbnails); + break; case MENU_ENUM_LABEL_MOUSE_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_mouse_enable); break; diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index c37f93ab55..a821ff8ff3 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -992,7 +992,7 @@ static void materialui_render_label_value( if (texture_switch) { - /* This will be used instead of label_color if + /* This will be used instead of label_color if * texture_switch is 'off' icon */ float pure_white[16]= { 1.00, 1.00, 1.00, 1.00, @@ -1568,7 +1568,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) { materialui_draw_tab_begin(mui, video_info, - width, height, + width, height, footer_bg_color ? &footer_bg_color[0] : NULL, &grey_bg[0]); @@ -1860,6 +1860,7 @@ static bool materialui_load_image(void *userdata, void *data, enum menu_image_ty menu_display_allocate_white_texture(); break; case MENU_IMAGE_THUMBNAIL: + case MENU_IMAGE_LEFT_THUMBNAIL: case MENU_IMAGE_SAVESTATE_THUMBNAIL: break; } diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 532cfac8bf..33027c3e77 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -2,6 +2,7 @@ * Copyright (C) 2011-2017 - Daniel De Matteis * Copyright (C) 2014-2017 - Jean-André Santoni * Copyright (C) 2016-2017 - Brad Parker + * Copyright (C) 2018 - Alfredo Monclús * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- @@ -196,12 +197,15 @@ typedef struct xmb_handle unsigned categories_active_idx; unsigned categories_active_idx_old; uintptr_t thumbnail; + uintptr_t left_thumbnail; uintptr_t savestate_thumbnail; float x; float alpha; float thumbnail_width; float thumbnail_height; + float left_thumbnail_width; + float left_thumbnail_height; float savestate_thumbnail_width; float savestate_thumbnail_height; float above_subitem_offset; @@ -243,6 +247,7 @@ typedef struct xmb_handle char *thumbnail_content; char *savestate_thumbnail_file_path; char *thumbnail_file_path; + char *left_thumbnail_file_path; char *bg_file_path; file_list_t *selection_buf_old; @@ -506,11 +511,17 @@ static xmb_node_t *xmb_copy_node(const xmb_node_t *old_node) return new_node; } -static const char *xmb_thumbnails_ident(void) +static const char *xmb_thumbnails_ident(char pos) { + char folder; settings_t *settings = config_get_ptr(); - switch (settings->uints.menu_thumbnails) + if (pos == 'R') + folder = settings->uints.menu_thumbnails; + if (pos == 'L') + folder = settings->uints.menu_left_thumbnails; + + switch (folder) { case 1: return "Named_Snaps"; @@ -981,7 +992,7 @@ end: string_list_free(list); } -static void xmb_update_thumbnail_path(void *data, unsigned i) +static void xmb_update_thumbnail_path(void *data, unsigned i, char pos) { menu_entry_t entry; unsigned entry_type = 0; @@ -1034,10 +1045,19 @@ static void xmb_update_thumbnail_path(void *data, unsigned i) if (string_is_equal(core_name, "imageviewer")) { - if (!string_is_empty(entry.label)) - strlcpy(new_path, entry.label, - sizeof(new_path)); - goto end; + if (pos == 'R' || (pos == 'L' && string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))) + { + if (!string_is_empty(entry.label)) + strlcpy(new_path, entry.label, + sizeof(new_path)); + goto end; + } + else + { + xmb->left_thumbnail = 0; + goto end; + } } } @@ -1056,9 +1076,13 @@ static void xmb_update_thumbnail_path(void *data, unsigned i) tmp_new2[0] = '\0'; - /* Append Named_Snaps/Named_Boxart/Named_Titles */ - fill_pathname_join(tmp_new2, new_path, - xmb_thumbnails_ident(), PATH_MAX_LENGTH * sizeof(char)); + /* Append Named_Snaps/Named_Boxarts/Named_Titles */ + if (pos == 'R') + fill_pathname_join(tmp_new2, new_path, + xmb_thumbnails_ident('R'), PATH_MAX_LENGTH * sizeof(char)); + if (pos == 'L') + fill_pathname_join(tmp_new2, new_path, + xmb_thumbnails_ident('L'), PATH_MAX_LENGTH * sizeof(char)); strlcpy(new_path, tmp_new2, PATH_MAX_LENGTH * sizeof(char)); @@ -1104,7 +1128,13 @@ static void xmb_update_thumbnail_path(void *data, unsigned i) end: if (xmb && !string_is_empty(new_path)) - xmb->thumbnail_file_path = strdup(new_path); + { + if (pos == 'R') + xmb->thumbnail_file_path = strdup(new_path); + if (pos == 'L') + xmb->left_thumbnail_file_path = strdup(new_path); + } + menu_entry_free(&entry); } @@ -1170,17 +1200,32 @@ static void xmb_update_savestate_thumbnail_path(void *data, unsigned i) static void xmb_update_thumbnail_image(void *data) { xmb_handle_t *xmb = (xmb_handle_t*)data; - if (!xmb || string_is_empty(xmb->thumbnail_file_path)) + if (!xmb) return; - if (filestream_exists(xmb->thumbnail_file_path)) - task_push_image_load(xmb->thumbnail_file_path, - menu_display_handle_thumbnail_upload, NULL); - else - xmb->thumbnail = 0; + if (!(string_is_empty(xmb->thumbnail_file_path))) + { + if (filestream_exists(xmb->thumbnail_file_path)) + task_push_image_load(xmb->thumbnail_file_path, + menu_display_handle_thumbnail_upload, NULL); + else + xmb->thumbnail = 0; - free(xmb->thumbnail_file_path); - xmb->thumbnail_file_path = NULL; + free(xmb->thumbnail_file_path); + xmb->thumbnail_file_path = NULL; + } + + if (!(string_is_empty(xmb->left_thumbnail_file_path))) + { + if (filestream_exists(xmb->left_thumbnail_file_path)) + task_push_image_load(xmb->left_thumbnail_file_path, + menu_display_handle_left_thumbnail_upload, NULL); + else + xmb->left_thumbnail = 0; + + free(xmb->left_thumbnail_file_path); + xmb->left_thumbnail_file_path = NULL; + } } static void xmb_set_thumbnail_system(void *data, char*s, size_t len) @@ -1248,7 +1293,8 @@ static void xmb_selection_pointer_changed( menu_list_t *menu_list = NULL; file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); size_t selection = menu_navigation_get_selection(); - const char *thumb_ident = xmb_thumbnails_ident(); + const char *thumb_ident = xmb_thumbnails_ident('R'); + const char *lft_thumb_ident= xmb_thumbnails_ident('L'); menu_entries_ctl(MENU_ENTRIES_CTL_LIST_GET, &menu_list); menu_entry_init(&entry); @@ -1290,8 +1336,8 @@ static void xmb_selection_pointer_changed( ia = xmb->items_active_alpha; iz = xmb->items_active_zoom; - if (!string_is_equal(thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) || !string_is_equal(lft_thumb_ident, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { if ((xmb_system_tab > XMB_SYSTEM_TAB_SETTINGS && depth == 1) || @@ -1299,8 +1345,18 @@ static void xmb_selection_pointer_changed( { if (!string_is_empty(entry.path)) xmb_set_thumbnail_content(xmb, entry.path, 0 /* will be ignored */); - xmb_update_thumbnail_path(xmb, i); - xmb_update_thumbnail_image(xmb); + if (!string_is_equal(thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + xmb_update_thumbnail_path(xmb, i, 'R'); + xmb_update_thumbnail_image(xmb); + } + if (!string_is_equal(lft_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + xmb_update_thumbnail_path(xmb, i, 'L'); + xmb_update_thumbnail_image(xmb); + } } else if (((entry_type == FILE_TYPE_IMAGE || entry_type == FILE_TYPE_IMAGEVIEWER || entry_type == FILE_TYPE_RDB || entry_type == FILE_TYPE_RDB_ENTRY) @@ -1308,14 +1364,34 @@ static void xmb_selection_pointer_changed( { if (!string_is_empty(entry.path)) xmb_set_thumbnail_content(xmb, entry.path, 0 /* will be ignored */); - xmb_update_thumbnail_path(xmb, i); - xmb_update_thumbnail_image(xmb); + if (!string_is_equal(thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + xmb_update_thumbnail_path(xmb, i, 'R'); + xmb_update_thumbnail_image(xmb); + } + else if (!string_is_equal(lft_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + xmb_update_thumbnail_path(xmb, i, 'L'); + xmb_update_thumbnail_image(xmb); + } } else if (filebrowser_get_type() != FILEBROWSER_NONE) { xmb_reset_thumbnail_content(xmb); - xmb_update_thumbnail_path(xmb, i); - xmb_update_thumbnail_image(xmb); + if (!string_is_equal(thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + xmb_update_thumbnail_path(xmb, i, 'R'); + xmb_update_thumbnail_image(xmb); + } + else if (!string_is_equal(lft_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + xmb_update_thumbnail_path(xmb, i, 'L'); + xmb_update_thumbnail_image(xmb); + } } } xmb_update_savestate_thumbnail_path(xmb, i); @@ -1501,7 +1577,9 @@ static void xmb_list_open_new(xmb_handle_t *xmb, { if (xmb->depth < 4) xmb_reset_thumbnail_content(xmb); - xmb_update_thumbnail_path(xmb, 0); + xmb_update_thumbnail_path(xmb, 0, 'R'); + xmb_update_thumbnail_image(xmb); + xmb_update_thumbnail_path(xmb, 0, 'L'); xmb_update_thumbnail_image(xmb); } } @@ -1811,7 +1889,7 @@ static void xmb_list_switch(xmb_handle_t *xmb) xmb_list_switch_new(xmb, selection_buf, dir, selection); xmb->categories_active_idx_old = (unsigned)xmb->categories_selection_ptr; - if (!string_is_equal(xmb_thumbnails_ident(), + if (!string_is_equal(xmb_thumbnails_ident('R'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { menu_entry_t entry; @@ -1824,7 +1902,23 @@ static void xmb_list_switch(xmb_handle_t *xmb) menu_entry_free(&entry); - xmb_update_thumbnail_path(xmb, 0); + xmb_update_thumbnail_path(xmb, 0, 'R'); + xmb_update_thumbnail_image(xmb); + } + if (!string_is_equal(xmb_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + menu_entry_t entry; + + menu_entry_init(&entry); + menu_entry_get(&entry, 0, selection, NULL, true); + + if (!string_is_empty(entry.path)) + xmb_set_thumbnail_content(xmb, entry.path, 0 /* will be ignored */); + + menu_entry_free(&entry); + + xmb_update_thumbnail_path(xmb, 0, 'L'); xmb_update_thumbnail_image(xmb); } } @@ -2159,10 +2253,13 @@ static void xmb_populate_entries(void *data, { xmb_selection_pointer_changed(xmb, false); menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL); - if (!string_is_equal(xmb_thumbnails_ident(), + if (!string_is_equal(xmb_thumbnails_ident('R'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) xmb_update_thumbnail_image(xmb); xmb_update_savestate_thumbnail_image(xmb); + if (!string_is_equal(xmb_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + xmb_update_thumbnail_image(xmb); return; } @@ -2384,6 +2481,7 @@ static int xmb_draw_item( file_list_t *list, float *color, const char *thumb_ident, + const char *left_thumb_ident, uint64_t frame_count, size_t i, size_t current, @@ -2489,7 +2587,11 @@ static int xmb_draw_item( (!string_is_equal (thumb_ident, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) - && xmb->thumbnail) + && xmb->thumbnail) || + (!string_is_equal + (left_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) + && xmb->left_thumbnail) ) ticker_limit = 40 * scale_mod[1]; else @@ -2640,7 +2742,8 @@ static void xmb_draw_items( xmb_node_t *core_node = NULL; size_t end = 0; uint64_t frame_count = xmb ? xmb->frame_count : 0; - const char *thumb_ident = xmb_thumbnails_ident(); + const char *thumb_ident = xmb_thumbnails_ident('R'); + const char *left_thumb_ident= xmb_thumbnails_ident('L'); if (!list || !list->size || !xmb) return; @@ -2690,7 +2793,7 @@ static void xmb_draw_items( &entry, &mymat, xmb, core_node, - list, color, thumb_ident, + list, color, thumb_ident, left_thumb_ident, frame_count, i, current, width, height); @@ -2927,7 +3030,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) size_t percent_width = 0; math_matrix_4x4 mymat; unsigned i; - float thumb_width, thumb_height; + float thumb_width, thumb_height, left_thumb_width, left_thumb_height; menu_display_ctx_rotate_draw_t rotate_draw; char msg[1024]; char title_msg[255]; @@ -3019,7 +3122,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb->savestate_thumbnail_width, xmb->savestate_thumbnail_height, xmb->savestate_thumbnail); else if (xmb->thumbnail - && !string_is_equal(xmb_thumbnails_ident(), + && !string_is_equal(xmb_thumbnails_ident('R'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { #ifdef XMB_DEBUG @@ -3028,7 +3131,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) #endif /* Limit thumbnail height to screen height + margin. */ - if( xmb->margins_screen_top + xmb->icon_size + xmb->thumbnail_height * scale_mod[4] >= + if( xmb->margins_screen_top + xmb->icon_size + xmb->thumbnail_height * scale_mod[4] >= (float)(height * 0.96) ) { thumb_width = xmb->thumbnail_width * @@ -3051,6 +3154,40 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb->margins_screen_top + xmb->icon_size + thumb_height * scale_mod[4], thumb_width, thumb_height, xmb->thumbnail); + + } + + /* Left Thumbnail */ + + if (xmb->left_thumbnail + && !string_is_equal(xmb_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + + /* Limit thumbnail height to screen height + margin. */ + + if( xmb->margins_screen_top + xmb->icon_size * 2.2 + xmb->left_thumbnail_height >= + (float)(height * 0.9) ) + { + left_thumb_width = (xmb->left_thumbnail_width / scale_mod[4]) * + (((float)(height * 0.9) - xmb->margins_screen_top - xmb->icon_size * 2.2) / + (xmb->left_thumbnail_height * scale_mod[4])); + left_thumb_height = (xmb->left_thumbnail_height / scale_mod[4]) * + (((float)(height * 0.9) - xmb->margins_screen_top - xmb->icon_size * 2.2) / + (xmb->left_thumbnail_height * scale_mod[4])); + } + else + { + left_thumb_width = xmb->left_thumbnail_width / scale_mod[4]; + left_thumb_height = xmb->left_thumbnail_height / scale_mod[4]; + } + + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + xmb->margins_title_left - 10, + xmb->margins_screen_top + xmb->icon_size * 2.2 + left_thumb_height * scale_mod[4], + left_thumb_width, left_thumb_height, + xmb->left_thumbnail); } /* Clock image */ @@ -3334,6 +3471,7 @@ static void xmb_layout_ps3(xmb_handle_t *xmb, int width) xmb->thumbnail_width = 460.0 * scale_factor; + xmb->left_thumbnail_width = 430.0 * scale_factor; xmb->savestate_thumbnail_width= 460.0 * scale_factor; xmb->cursor_size = 64.0 * scale_factor; @@ -3409,6 +3547,7 @@ static void xmb_layout_psp(xmb_handle_t *xmb, int width) xmb->margins_screen_top = (256+32) * scale_factor; xmb->thumbnail_width = 460.0 * scale_factor; + xmb->left_thumbnail_width = 400.0 * scale_factor; xmb->savestate_thumbnail_width= 460.0 * scale_factor; xmb->cursor_size = 64.0; @@ -3710,6 +3849,8 @@ static void xmb_free(void *data) free(xmb->savestate_thumbnail_file_path); if (!string_is_empty(xmb->thumbnail_file_path)) free(xmb->thumbnail_file_path); + if (!string_is_empty(xmb->left_thumbnail_file_path)) + free(xmb->left_thumbnail_file_path); if (!string_is_empty(xmb->bg_file_path)) free(xmb->bg_file_path); } @@ -3754,6 +3895,16 @@ static bool xmb_load_image(void *userdata, void *data, enum menu_image_type type TEXTURE_FILTER_MIPMAP_LINEAR, &xmb->thumbnail); } break; + case MENU_IMAGE_LEFT_THUMBNAIL: + { + struct texture_image *img = (struct texture_image*)data; + xmb->left_thumbnail_height = xmb->left_thumbnail_width + * (float)img->height / (float)img->width; + video_driver_texture_unload(&xmb->left_thumbnail); + video_driver_texture_load(data, + TEXTURE_FILTER_MIPMAP_LINEAR, &xmb->left_thumbnail); + } + break; case MENU_IMAGE_SAVESTATE_THUMBNAIL: { struct texture_image *img = (struct texture_image*)data; @@ -4008,7 +4159,10 @@ static void xmb_context_reset(void *data, bool is_threaded) xmb_context_reset_background(iconpath); xmb_context_reset_horizontal_list(xmb); - if (!string_is_equal(xmb_thumbnails_ident(), + if (!string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + xmb_update_thumbnail_image(xmb); + if (!string_is_equal(xmb_thumbnails_ident('R'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) xmb_update_thumbnail_image(xmb); xmb_update_savestate_thumbnail_image(xmb); @@ -4316,6 +4470,7 @@ static void xmb_context_destroy(void *data) video_driver_texture_unload(&xmb->textures.list[i]); video_driver_texture_unload(&xmb->thumbnail); + video_driver_texture_unload(&xmb->left_thumbnail); video_driver_texture_unload(&xmb->savestate_thumbnail); xmb_context_destroy_horizontal_list(xmb); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 4ff133aeaa..6141c00bc7 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -505,14 +505,14 @@ static int menu_displaylist_parse_system_info(menu_displaylist_info_t *info) MENU_SETTINGS_CORE_INFO_NONE, 0, 0); snprintf(tmp, sizeof(tmp), "Port #%d device display name: %s", controller, - input_config_get_device_display_name(controller) ? + input_config_get_device_display_name(controller) ? input_config_get_device_display_name(controller) : "N/A"); menu_entries_append_enum(info->list, tmp, "", MENU_ENUM_LABEL_SYSTEM_INFO_ENTRY, MENU_SETTINGS_CORE_INFO_NONE, 0, 0); snprintf(tmp, sizeof(tmp), "Port #%d device config name: %s", controller, - input_config_get_device_display_name(controller) ? + input_config_get_device_display_name(controller) ? input_config_get_device_config_name(controller) : "N/A"); menu_entries_append_enum(info->list, tmp, "", MENU_ENUM_LABEL_SYSTEM_INFO_ENTRY, @@ -2596,7 +2596,7 @@ static int menu_displaylist_parse_load_content_settings( } - if (settings->bools.quick_menu_show_save_load_state + if (settings->bools.quick_menu_show_save_load_state #ifdef HAVE_CHEEVOS && !(settings->bools.cheevos_hardcore_mode_enable && cheevos_loaded) #endif @@ -5339,6 +5339,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_THUMBNAILS, PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_LEFT_THUMBNAILS, + PARSE_ONLY_UINT, false); info->need_refresh = true; info->need_push = true; @@ -6162,7 +6165,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) case DISPLAYLIST_LOAD_CONTENT_LIST: case DISPLAYLIST_LOAD_CONTENT_SPECIAL: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - + if (!string_is_empty(settings->paths.directory_menu_content)) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES), @@ -6655,17 +6658,17 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) info->type_default = FILE_TYPE_SHADER_PRESET; if (video_shader_is_supported(RARCH_SHADER_CG) && - video_shader_get_type_from_ext("cgp", &is_preset) + video_shader_get_type_from_ext("cgp", &is_preset) != RARCH_SHADER_NONE) string_list_append(str_list, "cgp", attr); if (video_shader_is_supported(RARCH_SHADER_GLSL) && - video_shader_get_type_from_ext("glslp", &is_preset) + video_shader_get_type_from_ext("glslp", &is_preset) != RARCH_SHADER_NONE) string_list_append(str_list, "glslp", attr); if (video_shader_is_supported(RARCH_SHADER_SLANG) && - video_shader_get_type_from_ext("slangp", &is_preset) + video_shader_get_type_from_ext("slangp", &is_preset) != RARCH_SHADER_NONE) string_list_append(str_list, "slangp", attr); string_list_join_concat(new_exts, sizeof(new_exts), str_list, "|"); @@ -6694,17 +6697,17 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) if (video_shader_is_supported(RARCH_SHADER_CG) && - video_shader_get_type_from_ext("cg", &is_preset) + video_shader_get_type_from_ext("cg", &is_preset) != RARCH_SHADER_NONE) string_list_append(str_list, "cg", attr); if (video_shader_is_supported(RARCH_SHADER_GLSL) && - video_shader_get_type_from_ext("glsl", &is_preset) + video_shader_get_type_from_ext("glsl", &is_preset) != RARCH_SHADER_NONE) string_list_append(str_list, "glsl", attr); if (video_shader_is_supported(RARCH_SHADER_SLANG) && - video_shader_get_type_from_ext("slang", &is_preset) + video_shader_get_type_from_ext("slang", &is_preset) != RARCH_SHADER_NONE) string_list_append(str_list, "slang", attr); diff --git a/menu/menu_driver.c b/menu/menu_driver.c index c335abad97..128dd37bdc 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -629,7 +629,7 @@ void menu_display_draw_bg(menu_display_ctx_draw_t *draw, coords.vertex = new_vertex; coords.tex_coord = new_tex_coord; coords.lut_tex_coord = new_tex_coord; - coords.color = (const float*)draw->color; + coords.color = (const float*)draw->color; draw->coords = &coords; draw->scale_factor = 1.0f; @@ -1023,9 +1023,9 @@ void menu_display_rotate_z(menu_display_ctx_rotate_draw_t *draw, math_matrix_4x4 *b = NULL; if ( - !draw || - !menu_disp || - !menu_disp->get_default_mvp || + !draw || + !menu_disp || + !menu_disp->get_default_mvp || menu_disp->handles_transform ) return; @@ -1074,6 +1074,22 @@ void menu_display_handle_thumbnail_upload(void *task_data, free(user_data); } +void menu_display_handle_left_thumbnail_upload(void *task_data, + void *user_data, const char *err) +{ + menu_ctx_load_image_t load_image_info; + struct texture_image *img = (struct texture_image*)task_data; + + load_image_info.data = img; + load_image_info.type = MENU_IMAGE_LEFT_THUMBNAIL; + + menu_driver_load_image(&load_image_info); + + image_texture_free(img); + free(img); + free(user_data); +} + void menu_display_handle_savestate_thumbnail_upload(void *task_data, void *user_data, const char *err) { @@ -2183,7 +2199,8 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) if (!menu_driver_ctx || !menu_driver_ctx->update_thumbnail_path) return false; - menu_driver_ctx->update_thumbnail_path(menu_userdata, (unsigned)selection); + menu_driver_ctx->update_thumbnail_path(menu_userdata, (unsigned)selection, 'L'); + menu_driver_ctx->update_thumbnail_path(menu_userdata, (unsigned)selection, 'R'); } break; case RARCH_MENU_CTL_UPDATE_THUMBNAIL_IMAGE: diff --git a/menu/menu_driver.h b/menu/menu_driver.h index ed2489c0b1..3d8a2a74b0 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -61,6 +61,7 @@ enum menu_image_type MENU_IMAGE_NONE = 0, MENU_IMAGE_WALLPAPER, MENU_IMAGE_THUMBNAIL, + MENU_IMAGE_LEFT_THUMBNAIL, MENU_IMAGE_SAVESTATE_THUMBNAIL }; @@ -491,7 +492,7 @@ typedef struct menu_ctx_driver int (*pointer_tap)(void *data, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action); - void (*update_thumbnail_path)(void *data, unsigned i); + void (*update_thumbnail_path)(void *data, unsigned i, char pos); void (*update_thumbnail_image)(void *data); void (*set_thumbnail_system)(void *data, char* s, size_t len); void (*set_thumbnail_content)(void *data, char* s, size_t len); @@ -721,6 +722,9 @@ void menu_display_handle_wallpaper_upload(void *task_data, void menu_display_handle_thumbnail_upload(void *task_data, void *user_data, const char *err); +void menu_display_handle_left_thumbnail_upload(void *task_data, + void *user_data, const char *err); + void menu_display_handle_savestate_thumbnail_upload(void *task_data, void *user_data, const char *err); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 55cda7d72d..f076d5bd2e 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -1523,7 +1523,7 @@ static void get_string_representation_bind_device(void * data, char *s, if (map < max_devices) { - const char *device_name = input_config_get_device_display_name(map) ? + const char *device_name = input_config_get_device_display_name(map) ? input_config_get_device_display_name(map) : input_config_get_device_name(map); if (!string_is_empty(device_name)) @@ -5545,7 +5545,7 @@ static bool setting_append_list( if (string_is_equal(settings->arrays.menu_driver, "glui")) { - /* only GLUI uses these values, don't show + /* only GLUI uses these values, don't show * them on other drivers */ CONFIG_BOOL( list, list_info, @@ -5579,7 +5579,7 @@ static bool setting_append_list( #ifdef HAVE_XMB if (string_is_equal(settings->arrays.menu_driver, "xmb")) { - /* only XMB uses these values, don't show + /* only XMB uses these values, don't show * them on other drivers. */ CONFIG_UINT( list, list_info, @@ -5814,7 +5814,7 @@ static bool setting_append_list( general_write_handler, general_read_handler, SD_FLAG_NONE); - + #ifdef HAVE_LAKKA CONFIG_BOOL( list, list_info, @@ -5831,7 +5831,7 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); #endif - + #ifdef HAVE_XMB if (string_is_equal(settings->arrays.menu_driver, "xmb")) { @@ -5850,7 +5850,7 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); - + CONFIG_STRING( list, list_info, settings->paths.menu_content_show_settings_password, @@ -5866,7 +5866,7 @@ static bool setting_append_list( settings_data_list_current_add_flags(list, list_info, SD_FLAG_ALLOW_INPUT | SD_FLAG_LAKKA_ADVANCED); } #endif - + CONFIG_BOOL( list, list_info, &settings->bools.menu_content_show_favorites, @@ -5984,7 +5984,7 @@ static bool setting_append_list( #ifdef HAVE_MATERIALUI if (string_is_equal(settings->arrays.menu_driver, "glui")) { - /* only MaterialUI uses these values, don't show + /* only MaterialUI uses these values, don't show * them on other drivers. */ CONFIG_BOOL( list, list_info, @@ -6073,6 +6073,19 @@ static bool setting_append_list( general_write_handler, general_read_handler); menu_settings_list_current_add_range(list, list_info, 0, 3, 1, true, true); + + CONFIG_UINT( + list, list_info, + &settings->uints.menu_left_thumbnails, + MENU_ENUM_LABEL_LEFT_THUMBNAILS, + MENU_ENUM_LABEL_VALUE_LEFT_THUMBNAILS, + menu_left_thumbnails_default, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + menu_settings_list_current_add_range(list, list_info, 0, 3, 1, true, true); } CONFIG_BOOL( diff --git a/msg_hash.h b/msg_hash.h index 59c330a01a..781fb6d757 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -184,7 +184,7 @@ enum msg_hash_enums MSG_NETPLAY_CHANGED_NICK, MSG_ADDED_TO_FAVORITES, MSG_RESET_CORE_ASSOCIATION, - MSG_CORE_ASSOCIATION_RESET, + MSG_CORE_ASSOCIATION_RESET, MSG_AUTODETECT, MSG_AUDIO_VOLUME, MSG_AUDIO_MIXER_VOLUME, @@ -773,6 +773,7 @@ enum msg_hash_enums MENU_LABEL(CONTENT_SHOW_ADD), MENU_LABEL(XMB_RIBBON_ENABLE), MENU_LABEL(THUMBNAILS), + MENU_LABEL(LEFT_THUMBNAILS), MENU_LABEL(TIMEDATE_ENABLE), MENU_LABEL(BATTERY_LEVEL_ENABLE), MENU_LABEL(MATERIALUI_MENU_COLOR_THEME), @@ -1718,6 +1719,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_CB_LAKKA_DOWNLOAD, MENU_ENUM_LABEL_CB_LAKKA_LIST, MENU_ENUM_LABEL_CB_MENU_THUMBNAIL, + MENU_ENUM_LABEL_CB_MENU_LEFT_THUMBNAIL, MENU_ENUM_LABEL_CB_MENU_SAVESTATE_THUMBNAIL, MENU_ENUM_LABEL_CB_MENU_WALLPAPER, MENU_ENUM_LABEL_CB_THUMBNAILS_UPDATER_DOWNLOAD, diff --git a/retroarch.cfg b/retroarch.cfg index f4639e8031..5517a67458 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -700,6 +700,7 @@ video_message_bgcolor_opacity = 1.0 # Type of thumbnail to display. 0 = none, 1 = snaps, 2 = titles, 3 = boxarts # menu_thumbnails = 0 +# menu_left_thumbnails = 0 # Wrap-around to beginning and/or end if boundary of list is reached horizontally or vertically. # menu_navigation_wraparound_enable = false From ca7c37430380d96d1e6d7a1174bb0452cc843912 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:41:02 +0200 Subject: [PATCH 035/517] Fix OSX compilation --- libretro-common/compat/unlink_utf8.c | 4 ++-- libretro-common/include/compat/unlink_utf8.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libretro-common/compat/unlink_utf8.c b/libretro-common/compat/unlink_utf8.c index 8ca47c7790..a6976b0577 100644 --- a/libretro-common/compat/unlink_utf8.c +++ b/libretro-common/compat/unlink_utf8.c @@ -1,7 +1,6 @@ #include #include -#include "boolean.h" -#include +#include #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) #ifndef LEGACY_WIN32 @@ -14,6 +13,7 @@ #define NOMINMAX #define WIN32_LEAN_AND_MEAN #include +#include bool unlink_utf8(const char * filename) { diff --git a/libretro-common/include/compat/unlink_utf8.h b/libretro-common/include/compat/unlink_utf8.h index 59a3d4c9f9..f3d3d7487f 100644 --- a/libretro-common/include/compat/unlink_utf8.h +++ b/libretro-common/include/compat/unlink_utf8.h @@ -1,7 +1,7 @@ #ifndef __UNLINK_UTF8_H #define __UNLINK_UTF8_H -#include "boolean.h" +#include #ifdef _WIN32 From 43475176040f639a8cadf574a40dbf35dd0aacaf Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:46:39 +0200 Subject: [PATCH 036/517] Buildfixes --- runahead/secondary_core.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 845299319c..08d4bdcefb 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -73,21 +73,16 @@ char* get_temp_directory_alloc(void) { #ifdef _WIN32 #ifdef LEGACY_WIN32 - DWORD pathLength; - char *path; + DWORD pathLength = GetTempPath(0, NULL) + 1; + char *path = (char*)malloc(pathLength * sizeof(char)); - pathLength = GetTempPath(0, NULL) + 1; - path = (char*)malloc(pathLength * sizeof(char)); path[pathLength - 1] = 0; GetTempPath(pathLength, path); return path; #else - DWORD pathLength; - wchar_t *wideStr; char *path; - - pathLength = GetTempPathW(0, NULL) + 1; - wideStr = (wchar_t*)malloc(pathLength * sizeof(wchar_t)); + DWORD pathLength = GetTempPathW(0, NULL) + 1; + wchar_t *wideStr = (wchar_t*)malloc(pathLength * sizeof(wchar_t)); wideStr[pathLength - 1] = 0; GetTempPathW(pathLength, wideStr); @@ -96,8 +91,7 @@ char* get_temp_directory_alloc(void) return path; #endif #else - char *path; - path = strcpy_alloc_force(getenv("TMPDIR"); + char *path = strcpy_alloc_force(getenv("TMPDIR"); return path; #endif } @@ -177,9 +171,9 @@ void* read_file_data_alloc(const char *fileName, int *size) fseek(f, 0, SEEK_END); #ifdef _WIN32 - fileSizeLong = _ftelli64(f); + fileSizeLong = _ftelli64(f); #else - fileSizeLong = ftello64(f); + fileSizeLong = ftello64(f); #endif fseek(f, 0, SEEK_SET); From 76b025d70e64c6b96389622df6f18c48620b8085 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:49:39 +0200 Subject: [PATCH 037/517] malloc.h not available on OSX --- runahead/dirty_input.c | 2 ++ runahead/mem_util.c | 2 ++ runahead/mem_util.h | 1 - runahead/secondary_core.c | 1 - 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/runahead/dirty_input.c b/runahead/dirty_input.c index 0c5b1b2ac4..f58c9c412a 100644 --- a/runahead/dirty_input.c +++ b/runahead/dirty_input.c @@ -1,3 +1,5 @@ +#include + #include #include "../core.h" diff --git a/runahead/mem_util.c b/runahead/mem_util.c index 7f7876f169..6d807236e7 100644 --- a/runahead/mem_util.c +++ b/runahead/mem_util.c @@ -1,3 +1,5 @@ +#include + #include "mem_util.h" void *malloc_zero(size_t size) diff --git a/runahead/mem_util.h b/runahead/mem_util.h index b02f8d8b20..d91047244f 100644 --- a/runahead/mem_util.h +++ b/runahead/mem_util.h @@ -1,7 +1,6 @@ #ifndef __MEM_UTIL__ #define __MEM_UTIL__ -#include #include #include diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 08d4bdcefb..524ebccbda 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -1,7 +1,6 @@ #if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC #include -#include #include #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) From 40594a3059d1c8a808f8356b8bf46edbd845d1dd Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:51:20 +0200 Subject: [PATCH 038/517] Buildfix --- runahead/secondary_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 524ebccbda..499efae6d6 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -90,7 +90,7 @@ char* get_temp_directory_alloc(void) return path; #endif #else - char *path = strcpy_alloc_force(getenv("TMPDIR"); + char *path = strcpy_alloc_force(getenv("TMPDIR")); return path; #endif } From c5ec1885f2d5a15a6a2a6c0389c548666240a64d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 15:52:49 +0200 Subject: [PATCH 039/517] Don't use malloc.h anymore --- runahead/copy_load_info.c | 2 +- runahead/mylist.c | 5 +++-- runahead/run_ahead.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c index c58db24360..19b19da53e 100644 --- a/runahead/copy_load_info.c +++ b/runahead/copy_load_info.c @@ -1,5 +1,5 @@ +#include #include -#include #include #include diff --git a/runahead/mylist.c b/runahead/mylist.c index de92552967..9402bcdee9 100644 --- a/runahead/mylist.c +++ b/runahead/mylist.c @@ -1,6 +1,7 @@ -#include "mylist.h" -#include +#include #include + +#include "mylist.h" #include "mem_util.h" void mylist_resize(MyList *list, int newSize, bool runConstructor) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index bf79fe1fa9..56d5e52a04 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include From b4859cde59d89f7cf18991171b8a657f062ac8d9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 16:12:56 +0200 Subject: [PATCH 040/517] Update copyright attributions as said to PR OP --- configuration.c | 1 - 1 file changed, 1 deletion(-) diff --git a/configuration.c b/configuration.c index 78fcea8d6c..c37036be3d 100644 --- a/configuration.c +++ b/configuration.c @@ -4,7 +4,6 @@ * Copyright (C) 2014-2017 - Jean-André Santoni * Copyright (C) 2015-2017 - Andrés Suárez * Copyright (C) 2016-2017 - Brad Parker - * Copyright (C) 2018 - Alfredo Monclús * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- From a8b810760b5c341813a622073e694d7e2b9cd37d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 16:18:45 +0200 Subject: [PATCH 041/517] (runahead) Get rid of a bunch of unnnecessary forward declarations --- runahead/run_ahead.c | 114 +++++++++++++++++++------------------------ runahead/run_ahead.h | 1 + 2 files changed, 52 insertions(+), 63 deletions(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 56d5e52a04..c11e7d30fb 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -7,26 +7,13 @@ #include "dirty_input.h" #include "mylist.h" #include "secondary_core.h" +#include "run_ahead.h" #include "../core.h" #include "../dynamic.h" #include "../audio/audio_driver.h" #include "../gfx/video_driver.h" -static void *runahead_save_state_alloc(void); -static void runahead_save_state_free(void *state); -static void runahead_save_state_list_init(size_t saveStateSize); -static void runahead_save_state_list_destroy(void); -static void runahead_save_state_list_rotate(void); - -static void add_hooks(void); -static void remove_hooks(void); -static void deinit_hook(void); -static void unload_hook(void); - -static void runahead_clear_variables(void); -static void runahead_check_for_gui(void); -static void runahead_error(void); static bool runahead_create(void); static bool runahead_save_state(void); static bool runahead_load_state(void); @@ -36,8 +23,6 @@ static void runahead_suspend_audio(void); static void runahead_resume_audio(void); static void runahead_suspend_video(void); static void runahead_resume_video(void); -void run_ahead(int runAheadCount, bool useSecondary); -void runahead_destroy(void); static size_t runahead_save_state_size = -1; @@ -46,9 +31,14 @@ static MyList *runahead_save_state_list; static void *runahead_save_state_alloc(void) { - retro_ctx_serialize_info_t *savestate; - savestate = (retro_ctx_serialize_info_t*)malloc(sizeof(retro_ctx_serialize_info_t)); + retro_ctx_serialize_info_t *savestate = (retro_ctx_serialize_info_t*) + malloc(sizeof(retro_ctx_serialize_info_t)); + + if (!savestate) + return NULL; + savestate->size = runahead_save_state_size; + if (runahead_save_state_size > 0 && runahead_save_state_size != -1) { savestate->data = malloc(runahead_save_state_size); @@ -56,10 +46,11 @@ static void *runahead_save_state_alloc(void) } else { - savestate->data = NULL; + savestate->data = NULL; savestate->data_const = NULL; - savestate->size = 0; + savestate->size = 0; } + return savestate; } @@ -84,6 +75,7 @@ static void runahead_save_state_list_destroy(void) mylist_destroy(&runahead_save_state_list); } +#if 0 static void runahead_save_state_list_rotate(void) { int i; @@ -91,11 +83,12 @@ static void runahead_save_state_list_rotate(void) void *firstElement; firstElement = runahead_save_state_list->data[0]; for (i = 1; i < runahead_save_state_list->size; i++) - { - runahead_save_state_list->data[i - 1] = runahead_save_state_list->data[i - 1]; - } - runahead_save_state_list->data[runahead_save_state_list->size - 1] = firstElement; + runahead_save_state_list->data[i - 1] = + runahead_save_state_list->data[i - 1]; + runahead_save_state_list->data[runahead_save_state_list->size - 1] = + firstElement; } +#endif /* Hooks - Hooks to cleanup, and add dirty input hooks */ @@ -106,8 +99,40 @@ typedef struct retro_core_t _retro_core_t; extern _retro_core_t current_core; extern struct retro_callbacks retro_ctx; -static void deinit_hook(void); -static void unload_hook(void); +static void remove_hooks(void) +{ + if (originalRetroDeinit) + { + current_core.retro_deinit = originalRetroDeinit; + originalRetroDeinit = NULL; + } + + if (originalRetroUnload) + { + current_core.retro_unload_game = originalRetroUnload; + originalRetroUnload = NULL; + } + remove_input_state_hook(); +} + +static void unload_hook(void) +{ + remove_hooks(); + runahead_destroy(); + secondary_core_destroy(); + if (current_core.retro_unload_game) + current_core.retro_unload_game(); +} + + +static void deinit_hook(void) +{ + remove_hooks(); + runahead_destroy(); + secondary_core_destroy(); + if (current_core.retro_deinit) + current_core.retro_deinit(); +} static void add_hooks(void) { @@ -126,43 +151,6 @@ static void add_hooks(void) add_input_state_hook(); } -static void remove_hooks(void) -{ - if (originalRetroDeinit) - { - current_core.retro_deinit = originalRetroDeinit; - originalRetroDeinit = NULL; - } - - if (originalRetroUnload) - { - current_core.retro_unload_game = originalRetroUnload; - originalRetroUnload = NULL; - } - remove_input_state_hook(); -} - -static void deinit_hook(void) -{ - remove_hooks(); - runahead_destroy(); - secondary_core_destroy(); - if (current_core.retro_deinit) - { - current_core.retro_deinit(); - } -} - -static void unload_hook(void) -{ - remove_hooks(); - runahead_destroy(); - secondary_core_destroy(); - if (current_core.retro_unload_game) - { - current_core.retro_unload_game(); - } -} /* Runahead Code */ diff --git a/runahead/run_ahead.h b/runahead/run_ahead.h index 77ef8b8654..967d21b5e8 100644 --- a/runahead/run_ahead.h +++ b/runahead/run_ahead.h @@ -7,6 +7,7 @@ RETRO_BEGIN_DECLS void runahead_destroy(void); + void run_ahead(int runAheadCount, bool useSecondary); RETRO_END_DECLS From 77f7e1da8cab97507c07dd48248e5a5ac5b45873 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 16:27:19 +0200 Subject: [PATCH 042/517] Make menu_driver_load_image a static function --- menu/menu_driver.c | 22 ++++++++++++++-------- menu/menu_driver.h | 8 -------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 128dd37bdc..7fc7abdcc6 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -61,6 +61,12 @@ #define PARTICLES_COUNT 100 +typedef struct menu_ctx_load_image +{ + void *data; + enum menu_image_type type; +} menu_ctx_load_image_t; + /* Menu drivers */ static const menu_ctx_driver_t *menu_ctx_drivers[] = { #if defined(HAVE_XUI) @@ -1058,6 +1064,14 @@ bool menu_display_get_tex_coords(menu_display_ctx_coord_draw_t *draw) return true; } +static bool menu_driver_load_image(menu_ctx_load_image_t *load_image_info) +{ + if (menu_driver_ctx && menu_driver_ctx->load_image) + return menu_driver_ctx->load_image(menu_userdata, + load_image_info->data, load_image_info->type); + return false; +} + void menu_display_handle_thumbnail_upload(void *task_data, void *user_data, const char *err) { @@ -1804,14 +1818,6 @@ void menu_driver_populate_entries(menu_displaylist_info_t *info) info->label, info->type); } -bool menu_driver_load_image(menu_ctx_load_image_t *load_image_info) -{ - if (menu_driver_ctx && menu_driver_ctx->load_image) - return menu_driver_ctx->load_image(menu_userdata, - load_image_info->data, load_image_info->type); - return false; -} - bool menu_driver_push_list(menu_ctx_displaylist_t *disp_list) { if (menu_driver_ctx->list_push) diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 3d8a2a74b0..b61131a33d 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -507,12 +507,6 @@ typedef struct menu_ctx_driver menu_entry_t *entry, unsigned action); } menu_ctx_driver_t; -typedef struct menu_ctx_load_image -{ - void *data; - enum menu_image_type type; -} menu_ctx_load_image_t; - typedef struct menu_ctx_displaylist { menu_displaylist_info_t *info; @@ -630,8 +624,6 @@ void menu_driver_navigation_set(bool scroll); void menu_driver_populate_entries(menu_displaylist_info_t *info); -bool menu_driver_load_image(menu_ctx_load_image_t *load_image_info); - bool menu_driver_push_list(menu_ctx_displaylist_t *disp_list); bool menu_driver_init(bool video_is_threaded); From 77f2b7d326fc924b0cc841d5c3bde094889934e4 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Thu, 29 Mar 2018 09:37:35 -0500 Subject: [PATCH 043/517] Replace the file handling code from secondary_core.c with the file streams Hide secondary instance menu item if dynamic libraries are not supported Remove unlink_utf8 stuff Fix a compiler warning --- griffin/griffin.c | 3 - libretro-common/compat/unlink_utf8.c | 30 ------ libretro-common/include/compat/unlink_utf8.h | 23 ----- menu/menu_displaylist.c | 2 + runahead/run_ahead.c | 2 +- runahead/secondary_core.c | 97 ++------------------ 6 files changed, 11 insertions(+), 146 deletions(-) delete mode 100644 libretro-common/compat/unlink_utf8.c delete mode 100644 libretro-common/include/compat/unlink_utf8.h diff --git a/griffin/griffin.c b/griffin/griffin.c index 5cf227c954..bb0d9121f6 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -56,9 +56,6 @@ COMPATIBILITY #include "../libretro-common/compat/compat_fnmatch.c" #include "../libretro-common/compat/fopen_utf8.c" -#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC -#include "../libretro-common/compat/unlink_utf8.c" -#endif #include "../libretro-common/memmap/memalign.c" /*============================================================ diff --git a/libretro-common/compat/unlink_utf8.c b/libretro-common/compat/unlink_utf8.c deleted file mode 100644 index a6976b0577..0000000000 --- a/libretro-common/compat/unlink_utf8.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include - -#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) -#ifndef LEGACY_WIN32 -#define LEGACY_WIN32 -#endif -#endif - -#ifdef _WIN32 - -#define NOMINMAX -#define WIN32_LEAN_AND_MEAN -#include -#include - -bool unlink_utf8(const char * filename) -{ -#if defined(LEGACY_WIN32) - bool result = DeleteFileA(filename); -#else - wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); - bool result = DeleteFileW(filename_w); - free(filename_w); -#endif - return result; -} - -#endif diff --git a/libretro-common/include/compat/unlink_utf8.h b/libretro-common/include/compat/unlink_utf8.h deleted file mode 100644 index f3d3d7487f..0000000000 --- a/libretro-common/include/compat/unlink_utf8.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __UNLINK_UTF8_H -#define __UNLINK_UTF8_H - -#include - -#ifdef _WIN32 - -#if __cplusplus -extern "C" -{ -#endif - -bool unlink_utf8(const char * filename); - -#if __cplusplus -} -#endif - -#else -#include -#define unlink_utf8 unlink -#endif -#endif diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 4ff133aeaa..b36c9c1dfb 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -4966,9 +4966,11 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, PARSE_ONLY_UINT, false); +#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, PARSE_ONLY_BOOL, false); +#endif if (settings->bools.menu_show_advanced_settings) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE, diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 56d5e52a04..ad57a9a576 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -92,7 +92,7 @@ static void runahead_save_state_list_rotate(void) firstElement = runahead_save_state_list->data[0]; for (i = 1; i < runahead_save_state_list->size; i++) { - runahead_save_state_list->data[i - 1] = runahead_save_state_list->data[i - 1]; + runahead_save_state_list->data[i - 1] = runahead_save_state_list->data[i]; } runahead_save_state_list->data[runahead_save_state_list->size - 1] = firstElement; } diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 499efae6d6..14fec12e7a 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -11,10 +11,9 @@ #include #include -#include -#include #include #include +#include #include "mem_util.h" @@ -41,12 +40,8 @@ static char* get_temp_directory_alloc(void); static char* copy_core_to_temp_file(void); -static void* read_file_data_alloc(const char *fileName, int *size); - -static bool write_file_data(const char *fileName, const void *data, int dataSize); - static bool write_file_with_random_name(char **tempDllPath, - const char *retroarchTempPath, const void* data, int dataSize); + const char *retroarchTempPath, const void* data, ssize_t dataSize); static void* InputListElementConstructor(void); @@ -66,8 +61,6 @@ void remember_controller_port_device(long port, long device); void clear_controller_port_map(void); -static void free_file(FILE **file_p); - char* get_temp_directory_alloc(void) { #ifdef _WIN32 @@ -102,7 +95,7 @@ char* copy_core_to_temp_file(void) char *retroarchTempPath = NULL; char *tempDllPath = NULL; void *dllFileData = NULL; - int dllFileSize = 0; + ssize_t dllFileSize = 0; const char *corePath = path_get(RARCH_PATH_CORE); const char *coreBaseName = path_basename(corePath); @@ -123,14 +116,12 @@ char* copy_core_to_temp_file(void) if (!okay) goto failed; - dllFileData = read_file_data_alloc(corePath, &dllFileSize); - - if (!dllFileData) + if (!file_stream_read_file(corePath, &dllFileData, &dllFileSize)) goto failed; strcat_alloc(&tempDllPath, retroarchTempPath); strcat_alloc(&tempDllPath, coreBaseName); - okay = write_file_data(tempDllPath, dllFileData, dllFileSize); + okay = file_stream_write_file(tempDllPath, dllFileData, dllFileSize); if (!okay) { @@ -153,80 +144,8 @@ failed: return NULL; } -void* read_file_data_alloc(const char *fileName, int *size) -{ - void *data = NULL; - int fileSize = 0; - size_t bytesRead = 0; -#ifdef _WIN32 - int64_t fileSizeLong = 0; -#else - off64_t fileSizeLong = 0; -#endif - FILE *f = (FILE*)fopen_utf8(fileName, "rb"); - - if (!f) - goto failed; - - fseek(f, 0, SEEK_END); -#ifdef _WIN32 - fileSizeLong = _ftelli64(f); -#else - fileSizeLong = ftello64(f); -#endif - fseek(f, 0, SEEK_SET); - - /* 256MB file size limit for DLL files */ - if (fileSizeLong < 0 || fileSizeLong > 256 * 1024 * 1024) - goto failed; - - fileSize = (int)fileSizeLong; - data = malloc(fileSize); - - if (!data) - goto failed; - - bytesRead = fread(data, 1, fileSize, f); - - if ((int)bytesRead != (int)fileSize) - goto failed; - -success: - free_file(&f); - if (size) - *size = fileSize; - return data; -failed: - free_ptr(&data); - free_file(&f); - if (size) - *size = 0; - return NULL; -} - -bool write_file_data(const char *fileName, const void *data, int dataSize) -{ - bool okay = false; - size_t bytesWritten = 0; - FILE *f = (FILE*)fopen_utf8(fileName, "wb"); - - if (!f) - goto failed; - bytesWritten = fwrite(data, 1, dataSize, f); - - if (bytesWritten != dataSize) - goto failed; - -success: - free_file(&f); - return true; -failed: - free_file(&f); - return false; -} - bool write_file_with_random_name(char **tempDllPath, - const char *retroarchTempPath, const void* data, int dataSize) + const char *retroarchTempPath, const void* data, ssize_t dataSize) { int i; char numberBuf[32]; @@ -258,7 +177,7 @@ bool write_file_with_random_name(char **tempDllPath, strcat_alloc(tempDllPath, prefix); strcat_alloc(tempDllPath, numberBuf); strcat_alloc(tempDllPath, ext); - okay = write_file_data(*tempDllPath, data, dataSize); + okay = write_file_stream(*tempDllPath, data, dataSize); if (okay) break; } @@ -408,7 +327,7 @@ void secondary_core_destroy(void) dylib_close(secondary_module); secondary_module = NULL; - unlink_utf8(secondary_library_path); + filestream_delete(secondary_library_path); free_str(&secondary_library_path); } } From 8859fa0213e01cc4fde613ffb82ff9d91bcaa463 Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Thu, 29 Mar 2018 16:39:38 +0200 Subject: [PATCH 044/517] XMB Left thumb dynamic positioning and scaling. --- menu/drivers/xmb.c | 47 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 33027c3e77..9561712dc5 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -747,8 +747,8 @@ static void xmb_draw_thumbnail( coords.tex_coord = NULL; coords.lut_tex_coord = NULL; - draw.width = w * scale_mod[4]; - draw.height = h * scale_mod[4]; + draw.width = w; + draw.height = h; draw.coords = &coords; draw.matrix_data = &mymat; draw.texture = texture; @@ -2587,11 +2587,7 @@ static int xmb_draw_item( (!string_is_equal (thumb_ident, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) - && xmb->thumbnail) || - (!string_is_equal - (left_thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) - && xmb->left_thumbnail) + && xmb->thumbnail) ) ticker_limit = 40 * scale_mod[1]; else @@ -3119,7 +3115,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) + xmb->icon_spacing_horizontal + xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4, xmb->margins_screen_top + xmb->icon_size + xmb->savestate_thumbnail_height * scale_mod[4], - xmb->savestate_thumbnail_width, xmb->savestate_thumbnail_height, + xmb->savestate_thumbnail_width, xmb->savestate_thumbnail_height * scale_mod[4], xmb->savestate_thumbnail); else if (xmb->thumbnail && !string_is_equal(xmb_thumbnails_ident('R'), @@ -3152,7 +3148,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + xmb->icon_spacing_horizontal*4 - xmb->icon_size / 4, xmb->margins_screen_top + xmb->icon_size + thumb_height * scale_mod[4], - thumb_width, thumb_height, + thumb_width * scale_mod[4], thumb_height * scale_mod[4], xmb->thumbnail); } @@ -3163,29 +3159,32 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) && !string_is_equal(xmb_thumbnails_ident('L'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { + float scale_factor = (settings->uints.menu_xmb_scale_factor * width) / (1920.0 * 100); - /* Limit thumbnail height to screen height + margin. */ - - if( xmb->margins_screen_top + xmb->icon_size * 2.2 + xmb->left_thumbnail_height >= - (float)(height * 0.9) ) + /* Limit left thumbnail height to screen height + margin. */ + if (xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + + xmb->left_thumbnail_height >= (float)(height - (96.0 * scale_factor))) { - left_thumb_width = (xmb->left_thumbnail_width / scale_mod[4]) * - (((float)(height * 0.9) - xmb->margins_screen_top - xmb->icon_size * 2.2) / - (xmb->left_thumbnail_height * scale_mod[4])); - left_thumb_height = (xmb->left_thumbnail_height / scale_mod[4]) * - (((float)(height * 0.9) - xmb->margins_screen_top - xmb->icon_size * 2.2) / - (xmb->left_thumbnail_height * scale_mod[4])); + left_thumb_width = xmb->left_thumbnail_width * + (((float)(height - (96.0 * scale_factor)) - xmb->margins_screen_top - + (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / + xmb->left_thumbnail_height); + + left_thumb_height = xmb->left_thumbnail_height * + (((float)(height - (96.0 * scale_factor)) - xmb->margins_screen_top - + (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / + xmb->left_thumbnail_height); } else { - left_thumb_width = xmb->left_thumbnail_width / scale_mod[4]; - left_thumb_height = xmb->left_thumbnail_height / scale_mod[4]; + left_thumb_width = xmb->left_thumbnail_width; + left_thumb_height = xmb->left_thumbnail_height; } xmb_draw_thumbnail(video_info, xmb, &coord_white[0], width, height, - xmb->margins_title_left - 10, - xmb->margins_screen_top + xmb->icon_size * 2.2 + left_thumb_height * scale_mod[4], + 20 * scale_factor + ((xmb->left_thumbnail_width - left_thumb_width) / 2), + xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + left_thumb_height, left_thumb_width, left_thumb_height, xmb->left_thumbnail); } @@ -3740,7 +3739,7 @@ static void *xmb_init(void **userdata, bool video_is_threaded) if (!xmb->selection_buf_old) goto error; - xmb->categories_active_idx = 0; + xmb->categories_active_idx = 0; xmb->categories_active_idx_old = 0; xmb->x = 0; xmb->categories_x_pos = 0; From 67c3fd88f403317e0a2cee8c6fc229dd9ef9d912 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 17:35:28 +0200 Subject: [PATCH 045/517] Buildfixes - wrong function names were used --- runahead/secondary_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 14fec12e7a..002cb614ff 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -95,7 +95,7 @@ char* copy_core_to_temp_file(void) char *retroarchTempPath = NULL; char *tempDllPath = NULL; void *dllFileData = NULL; - ssize_t dllFileSize = 0; + ssize_t dllFileSize = 0; const char *corePath = path_get(RARCH_PATH_CORE); const char *coreBaseName = path_basename(corePath); @@ -116,12 +116,12 @@ char* copy_core_to_temp_file(void) if (!okay) goto failed; - if (!file_stream_read_file(corePath, &dllFileData, &dllFileSize)) + if (!filestream_read_file(corePath, &dllFileData, &dllFileSize)) goto failed; strcat_alloc(&tempDllPath, retroarchTempPath); strcat_alloc(&tempDllPath, coreBaseName); - okay = file_stream_write_file(tempDllPath, dllFileData, dllFileSize); + okay = filestream_write_file(tempDllPath, dllFileData, dllFileSize); if (!okay) { @@ -177,7 +177,7 @@ bool write_file_with_random_name(char **tempDllPath, strcat_alloc(tempDllPath, prefix); strcat_alloc(tempDllPath, numberBuf); strcat_alloc(tempDllPath, ext); - okay = write_file_stream(*tempDllPath, data, dataSize); + okay = filestream_write(*tempDllPath, data, dataSize); if (okay) break; } From 0712c0ba998efa081b901a44bf2ab7667e9bef60 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 17:45:25 +0200 Subject: [PATCH 046/517] Fix call to filestream_write to filestream_write_file --- runahead/secondary_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 002cb614ff..cce380eca8 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -177,7 +177,7 @@ bool write_file_with_random_name(char **tempDllPath, strcat_alloc(tempDllPath, prefix); strcat_alloc(tempDllPath, numberBuf); strcat_alloc(tempDllPath, ext); - okay = filestream_write(*tempDllPath, data, dataSize); + okay = filestream_write_file(*tempDllPath, data, dataSize); if (okay) break; } From adb2274fc683f8cce8036f804bda90170b732563 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 17:55:06 +0200 Subject: [PATCH 047/517] - Enable HAVE_RUNAHEAD for MSVC 2003 - Remove some unreferenced labels --- pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj | 8 ++++---- runahead/copy_load_info.c | 8 ++++---- runahead/run_ahead.c | 2 +- runahead/secondary_core.c | 7 ++----- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj b/pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj index 443c10edce..3bcfe59ddf 100644 --- a/pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj +++ b/pkg/msvc/msvc-2003/RetroArch-msvc2003.vcproj @@ -21,7 +21,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\gfx\include"" - PreprocessorDefinitions="_WIN32;WINVER=0x0400;_WIN32_WINNT=0x0400;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;WANT_ZLIB;HAVE_DINPUT;HAVE_DSOUND;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_OVERLAY;HAVE_RGUI;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT;HAVE_D3D;HAVE_D3D8;DEBUG;_DEBUG;__STDC_CONSTANT_MACROS" + PreprocessorDefinitions="_WIN32;WINVER=0x0400;_WIN32_WINNT=0x0400;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;WANT_ZLIB;HAVE_DINPUT;HAVE_DSOUND;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_OVERLAY;HAVE_RGUI;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT;HAVE_D3D;HAVE_D3D8;DEBUG;_DEBUG;__STDC_CONSTANT_MACROS" MinimalRebuild="TRUE" BasicRuntimeChecks="3" RuntimeLibrary="5" @@ -70,7 +70,7 @@ size; i++) { @@ -49,10 +49,10 @@ static void free_string_list(struct string_list *dest) static struct string_list* clone_string_list(const struct string_list *src) { - int i; + unsigned i; struct string_list *dest = NULL; - if (src == NULL) + if (!src) return NULL; dest = (struct string_list*)malloc_zero(sizeof(struct string_list)); diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index e5171ca52c..fbc7601af8 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -273,7 +273,7 @@ void run_ahead(int runAheadCount, bool useSecondary) if (!runahead_load_state_secondary()) return; - for (frame_count = 0; frame_count < runAheadCount - 1; frame_count++) + for (frame_count = 0; frame_count < (unsigned)(runAheadCount - 1); frame_count++) { runahead_suspend_video(); runahead_suspend_audio(); diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index cce380eca8..c62ee99845 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -130,7 +130,7 @@ char* copy_core_to_temp_file(void) if (!okay) goto failed; } -success: + free_str(&tempDirectory); free_str(&retroarchTempPath); free_ptr(&dllFileData); @@ -181,12 +181,9 @@ bool write_file_with_random_name(char **tempDllPath, if (okay) break; } -success: + free_str(&ext); return true; -failed: - free_str(&ext); - return false; } void secondary_core_clear(void) From 8819b2a2f66fb3247f3a59be0e55fa2c6bb02fd7 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 18:02:18 +0200 Subject: [PATCH 048/517] (MSVC 2005/2010/2013/2015/2017) Add HAVE_RUNAHEAD --- pkg/msvc/msvc-2005/RetroArch-msvc2005.vcproj | 8 +++---- pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj | 16 ++++++------- pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj | 14 +++++++---- pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj | 24 ++++++++++++------- pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj | 24 ++++++++++++------- 5 files changed, 53 insertions(+), 33 deletions(-) diff --git a/pkg/msvc/msvc-2005/RetroArch-msvc2005.vcproj b/pkg/msvc/msvc-2005/RetroArch-msvc2005.vcproj index 7ccc60d88c..32b2f78810 100644 --- a/pkg/msvc/msvc-2005/RetroArch-msvc2005.vcproj +++ b/pkg/msvc/msvc-2005/RetroArch-msvc2005.vcproj @@ -42,7 +42,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\gfx\include";"$(INETSDK)\Include";"$(INETSDK)\Include";"$(DXSDK_DIR)\Include"" - PreprocessorDefinitions="_WIN32_WINNT=0x0410;_WIN32;RARCH_INTERNAL;HAVE_THREADS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_SHADERPIPELINE;HAVE_OPENGL;HAVE_CC_RESAMPLER;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;WANT_ZLIB;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_RTHREADS;HAVE_DYNAMIC;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT;__STDC_CONSTANT_MACROS" + PreprocessorDefinitions="_WIN32_WINNT=0x0410;_WIN32;RARCH_INTERNAL;HAVE_THREADS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_SHADERPIPELINE;HAVE_OPENGL;HAVE_CC_RESAMPLER;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_CHEEVOS;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;WANT_ZLIB;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_RTHREADS;HAVE_DYNAMIC;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT;__STDC_CONSTANT_MACROS" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" @@ -122,7 +122,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -223,7 +223,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -243,7 +243,7 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -266,7 +266,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_GLSL;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -292,7 +292,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3DX;HAVE_CG;HAVE_GLSL;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -319,7 +319,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_GLSL;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -345,7 +345,7 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_CG;HAVE_GLSL;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_CHEEVOS;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp diff --git a/pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj b/pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj index 2eca6af1a1..7eda564904 100644 --- a/pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj +++ b/pkg/msvc/msvc-2013/RetroArch-msvc2013.vcxproj @@ -84,8 +84,9 @@ Level3 Disabled - WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;_DEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) + WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;_DEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) $(SolutionDir)\..\..\gfx\include\dxsdk;$(SolutionDir)\..\..\gfx\include;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\glslang;$(SolutionDir)\..\..\deps\SPIRV-Cross;%(AdditionalIncludeDirectories) + OldStyle Console @@ -99,8 +100,9 @@ Level3 Disabled - WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_UPDATE_ASSETS;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;_DEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) + WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_UPDATE_ASSETS;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;_DEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) $(SolutionDir)\..\..\gfx\include\dxsdk;$(SolutionDir)\..\..\gfx\include;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\glslang;$(SolutionDir)\..\..\deps\SPIRV-Cross;%(AdditionalIncludeDirectories) + OldStyle Console @@ -116,8 +118,9 @@ MaxSpeed true true - WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;HAVE_MENU;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;NDEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_COMMAND;HAVE_NETWORK_CMD;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) + WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;HAVE_MENU;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;NDEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_COMMAND;HAVE_NETWORK_CMD;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) $(SolutionDir)\..\..\gfx\include\dxsdk;$(SolutionDir)\..\..\gfx\include;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\glslang;$(SolutionDir)\..\..\deps\SPIRV-Cross;%(AdditionalIncludeDirectories) + OldStyle Console @@ -135,8 +138,9 @@ MaxSpeed true true - WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_UPDATE_ASSETS;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;NDEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) + WIN32;WANT_GLSLANG;HAVE_DYNAMIC;HAVE_DYLIB;HAVE_SPIRV_CROSS;HAVE_MENU;HAVE_SLANG;HAVE_GLSLANG;HAVE_UPDATE_ASSETS;HAVE_XMB;HAVE_SHADERPIPELINE;HAVE_RGUI;HAVE_MATERIALUI;NDEBUG;_WINDOWS;HAVE_XAUDIO;HAVE_DSOUND;HAVE_DINPUT;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_OPENGL;HAVE_VULKAN;HAVE_GLSL;HAVE_THREADS;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_RJPEG;HAVE_RPNG;HAVE_ZLIB;WANT_ZLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_OVERLAY;HAVE_7ZIP;HAVE_LIBRETRODB;HAVE_STB_FONT;%(PreprocessorDefinitions) $(SolutionDir)\..\..\gfx\include\dxsdk;$(SolutionDir)\..\..\gfx\include;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\glslang;$(SolutionDir)\..\..\deps\SPIRV-Cross;%(AdditionalIncludeDirectories) + OldStyle Console @@ -159,4 +163,4 @@ - + \ No newline at end of file diff --git a/pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj b/pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj index f9f51b52a9..f58275b30d 100644 --- a/pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj +++ b/pkg/msvc/msvc-2015/RetroArch-msvc2015.vcxproj @@ -190,12 +190,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions + OldStyle Console @@ -209,12 +210,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions + OldStyle Console @@ -229,13 +231,14 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp /bigobj Fast StreamingSIMDExtensions2 + OldStyle Console @@ -249,12 +252,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions2 + OldStyle Console @@ -271,13 +275,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions true + OldStyle Console @@ -295,13 +300,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions true + OldStyle Console @@ -320,13 +326,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions2 true + OldStyle Console @@ -344,13 +351,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;HAVE_UPDATE_ASSETS;HAVE_SLANG;HAVE_GLSLANG;WANT_GLSLANG;HAVE_VULKAN;HAVE_SPIRV_CROSS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\gfx\include\dxsdk;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions2 true + OldStyle Console diff --git a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj index 824877fbb4..981d2f2fc9 100644 --- a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj +++ b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj @@ -191,12 +191,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions + OldStyle Console @@ -210,12 +211,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_FBO;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_FBO;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions + OldStyle Console @@ -230,12 +232,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions2 + OldStyle Console @@ -249,12 +252,13 @@ Level3 Disabled - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_FBO;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_FBO;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp Fast StreamingSIMDExtensions2 + OldStyle Console @@ -271,13 +275,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions true + OldStyle Console @@ -295,13 +300,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_MENU;HAVE_RGUI;HAVE_GL_SYNC;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions true + OldStyle Console @@ -320,13 +326,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions2 true + OldStyle Console @@ -344,13 +351,14 @@ MaxSpeed true true - WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT + WIN32;RARCH_INTERNAL;HAVE_CC_RESAMPLER;WANT_GLSLANG;HAVE_SLANG;HAVE_GLSLANG;HAVE_SPIRV_CROSS;HAVE_UPDATE_ASSETS;HAVE_D3D;HAVE_D3D9;HAVE_D3D10;HAVE_D3D11;HAVE_D3D12;HAVE_VULKAN;HAVE_CG;HAVE_GLSL;HAVE_CHEEVOS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_FBO;HAVE_ZLIB;HAVE_XMB;HAVE_SHADERPIPELINE;WANT_ZLIB;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_DINPUT;HAVE_XINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETWORKING;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_RPNG;HAVE_RJPEG;HAVE_RBMP;HAVE_RTGA;HAVE_IMAGEVIEWER;WANT_ZLIB;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC;HAVE_MENU;HAVE_7ZIP;HAVE_MATERIALUI;HAVE_LIBRETRODB;HAVE_STB_FONT $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\..\;$(CG_INC_PATH);$(MSBuildProjectDirectory)\..\..\..\deps\zlib;$(MSBuildProjectDirectory)\..\..\..\libretro-common\include;$(MSBuildProjectDirectory)\..\..\..\deps;$(MSBuildProjectDirectory)\..\..\..\deps\glslang;$(MSBuildProjectDirectory)\..\..\..\deps\SPIRV-Cross;$(MSBuildProjectDirectory)\..\..\..\deps\stb;$(MSBuildProjectDirectory)\..\..\..\gfx\include;%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp Fast StreamingSIMDExtensions2 true + OldStyle Console From 425e1d9043a37bcf49c9b2c66f032e3f43bece0f Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Thu, 29 Mar 2018 18:04:00 +0200 Subject: [PATCH 049/517] (OSX) Define HAVE_RUNAHEAD --- pkg/apple/RetroArch.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/apple/RetroArch.xcodeproj/project.pbxproj b/pkg/apple/RetroArch.xcodeproj/project.pbxproj index d3f84fbbc0..3b4f0dd4be 100644 --- a/pkg/apple/RetroArch.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch.xcodeproj/project.pbxproj @@ -497,6 +497,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.5; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = ( + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_FLAC", "-DHAVE_LROUND", @@ -557,6 +558,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.5; OTHER_CFLAGS = ( + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_FLAC", "-DHAVE_LROUND", From e85f11ea38866006ebb324c8f540a8e609d2d0f0 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 18:05:35 +0200 Subject: [PATCH 050/517] (runahead) Remove unused variable --- runahead/secondary_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index c62ee99845..42556a8abd 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -147,10 +147,9 @@ failed: bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, ssize_t dataSize) { - int i; + unsigned i; char numberBuf[32]; bool okay = false; - const int maxAttempts = 30; const char *prefix = "tmp"; time_t timeValue = time(NULL); unsigned int numberValue = (unsigned int)timeValue; From 2db0c21f52ebd3d6185d524cbb984273eca99edc Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Thu, 29 Mar 2018 18:10:12 +0200 Subject: [PATCH 051/517] Fix C11 typedef redefinition issues --- runahead/dirty_input.c | 3 +-- runahead/run_ahead.c | 1 - runahead/secondary_core.c | 5 +---- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/runahead/dirty_input.c b/runahead/dirty_input.c index f58c9c412a..9d25978359 100644 --- a/runahead/dirty_input.c +++ b/runahead/dirty_input.c @@ -19,8 +19,7 @@ typedef struct InputListElement_t int16_t state[36]; } InputListElement; -typedef struct retro_core_t _retro_core_t; -extern _retro_core_t current_core; +extern struct retro_core_t current_core; extern struct retro_callbacks retro_ctx; typedef bool(*LoadStateFunction)(const void*, size_t); diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index fbc7601af8..2522855a33 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -173,7 +173,6 @@ static void runahead_clear_variables(void) static void runahead_check_for_gui(void) { /* Hack: If we were in the GUI, force a resync. */ - bool is_dirty = false; bool is_alive, is_focused = false; uint64_t frame_count = 0; diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 42556a8abd..8912b4ea7c 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -24,12 +24,9 @@ static int port_map[16]; -typedef struct retro_core_t _retro_core_t; -typedef struct retro_callbacks retro_callbacks_t; - static char *secondary_library_path; static dylib_t secondary_module; -static _retro_core_t secondary_core; +static struct retro_core_t secondary_core; static struct retro_callbacks secondary_callbacks; extern retro_ctx_load_content_info_t *load_content_info; From 8703ad8374f652a35d60a2e0ff74b3c4e99449c2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 18:29:25 +0200 Subject: [PATCH 052/517] Get rid of free_str --- runahead/mem_util.c | 5 ----- runahead/mem_util.h | 1 - runahead/secondary_core.c | 20 ++++++++++---------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/runahead/mem_util.c b/runahead/mem_util.c index 6d807236e7..a350f14c6a 100644 --- a/runahead/mem_util.c +++ b/runahead/mem_util.c @@ -9,11 +9,6 @@ void *malloc_zero(size_t size) return ptr; } -void free_str(char **str_p) -{ - free_ptr((void**)str_p); -} - void free_ptr(void **data_p) { if (!data_p || !*data_p) diff --git a/runahead/mem_util.h b/runahead/mem_util.h index d91047244f..fedcf1ca70 100644 --- a/runahead/mem_util.h +++ b/runahead/mem_util.h @@ -12,7 +12,6 @@ RETRO_BEGIN_DECLS void *malloc_zero(size_t size); -void free_str(char **str_p); void free_ptr(void **data_p); char *strcpy_alloc(const char *sourceStr); char *strcpy_alloc_force(const char *sourceStr); diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 8912b4ea7c..3d66c4ed77 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -128,16 +128,16 @@ char* copy_core_to_temp_file(void) goto failed; } - free_str(&tempDirectory); - free_str(&retroarchTempPath); + free_ptr((void**)&tempDirectory); + free_ptr((void**)&retroarchTempPath); free_ptr(&dllFileData); return tempDllPath; failed: - free_str(&tempDirectory); - free_str(&retroarchTempPath); - free_str(&tempDllPath); - free_ptr(&dllFileData); + free_ptr((void**)&tempDirectory); + free_ptr((void**)&retroarchTempPath); + free_ptr((void**)&tempDllPath); + free_ptr((void**)&dllFileData); return NULL; } @@ -168,7 +168,7 @@ bool write_file_with_random_name(char **tempDllPath, numberValue = numberValue * 214013 + 2531011; number = (numberValue >> 14) % 100000; sprintf(numberBuf, "%05d", number); - free_str(tempDllPath); + free_ptr((void**)tempDllPath); strcat_alloc(tempDllPath, retroarchTempPath); strcat_alloc(tempDllPath, prefix); strcat_alloc(tempDllPath, numberBuf); @@ -178,7 +178,7 @@ bool write_file_with_random_name(char **tempDllPath, break; } - free_str(&ext); + free_ptr((void**)&ext); return true; } @@ -199,7 +199,7 @@ bool secondary_core_create(void) load_content_info->special) return false; - free_str(&secondary_library_path); + free_ptr((void**)&secondary_library_path); secondary_library_path = copy_core_to_temp_file(); if (!secondary_library_path) @@ -321,7 +321,7 @@ void secondary_core_destroy(void) dylib_close(secondary_module); secondary_module = NULL; filestream_delete(secondary_library_path); - free_str(&secondary_library_path); + free_ptr((void**)&secondary_library_path); } } From 65909fd1db52af577a26364163e01cac90948cb0 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 18:36:53 +0200 Subject: [PATCH 053/517] free_file no longer necessary --- runahead/secondary_core.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 3d66c4ed77..29dda8087c 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -339,18 +339,6 @@ void clear_controller_port_map(void) for (port = 0; port < 16; port++) port_map[port] = -1; } - -static void free_file(FILE **file_p) -{ - bool result = false; - if (!file_p || !*file_p) - return; - result = fclose(*file_p) != 0; - *file_p = NULL; - return; -} - - #else #include From 25e99ba75bd1dff9b8685a5d91cfa303e782da5b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 18:41:19 +0200 Subject: [PATCH 054/517] Cleanups --- runahead/run_ahead.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 2522855a33..9a72ec97af 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -37,18 +37,15 @@ static void *runahead_save_state_alloc(void) if (!savestate) return NULL; - savestate->size = runahead_save_state_size; + savestate->data = NULL; + savestate->data_const = NULL; + savestate->size = 0; if (runahead_save_state_size > 0 && runahead_save_state_size != -1) { - savestate->data = malloc(runahead_save_state_size); + savestate->data = malloc(runahead_save_state_size); savestate->data_const = savestate->data; - } - else - { - savestate->data = NULL; - savestate->data_const = NULL; - savestate->size = 0; + savestate->size = runahead_save_state_size; } return savestate; @@ -216,8 +213,9 @@ void run_ahead(int runAheadCount, bool useSecondary) /* TODO: multiple savestates for higher performance when not using secondary core */ for (frameNumber = 0; frameNumber <= runAheadCount; frameNumber++) { - lastFrame = frameNumber == runAheadCount; + lastFrame = frameNumber == runAheadCount; suspendedFrame = !lastFrame; + if (suspendedFrame) { runahead_suspend_audio(); @@ -341,7 +339,8 @@ static bool runahead_save_state(void) static bool runahead_load_state(void) { - retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; + retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*) + runahead_save_state_list->data[0]; bool lastDirty = input_is_dirty; bool okay = core_unserialize(serialize_info); input_is_dirty = lastDirty; From 5076569c81cacf15ae5f52924b6dc46c276dc351 Mon Sep 17 00:00:00 2001 From: alfrix Date: Thu, 29 Mar 2018 13:36:15 -0300 Subject: [PATCH 055/517] Limit right thumb width --- menu/drivers/xmb.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 9561712dc5..97de4fd4f8 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3127,20 +3127,36 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) #endif /* Limit thumbnail height to screen height + margin. */ + + thumb_width = xmb->thumbnail_width; + thumb_height = xmb->thumbnail_height; + float window_width = video_info->width; + float window_height = video_info->height; + const float around_thumb_margin = 0.96; + if( xmb->margins_screen_top + xmb->icon_size + xmb->thumbnail_height * scale_mod[4] >= - (float)(height * 0.96) ) + (window_height * around_thumb_margin) ) { - thumb_width = xmb->thumbnail_width * - (((float)(height * 0.96) - xmb->margins_screen_top - xmb->icon_size) / - (xmb->thumbnail_height * scale_mod[4])); - thumb_height = xmb->thumbnail_height * - (((float)(height * 0.96) - xmb->margins_screen_top - xmb->icon_size) / - (xmb->thumbnail_height * scale_mod[4])); + thumb_width = thumb_width * + (((window_height * around_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / + (thumb_height * scale_mod[4])); + thumb_height = thumb_height * + (((window_height * around_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / + (thumb_height * scale_mod[4])); } - else + + /* Limit thumbnail width */ + + if ( xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + + xmb->icon_spacing_horizontal*4 - xmb->icon_size / 4 + thumb_width * scale_mod[4] >= + (window_width * around_thumb_margin) ) { - thumb_width = xmb->thumbnail_width; - thumb_height = xmb->thumbnail_height; + thumb_height = thumb_height * + (((window_width * around_thumb_margin) - xmb->margins_screen_left * scale_mod[5] - xmb->icon_spacing_horizontal - + xmb->icon_spacing_horizontal * 4 + xmb->icon_size / 4) / (thumb_width * scale_mod[4])); + thumb_width = thumb_width * + (((window_width * around_thumb_margin) - xmb->margins_screen_left * scale_mod[5] - xmb->icon_spacing_horizontal - + xmb->icon_spacing_horizontal * 4 + xmb->icon_size / 4) / (thumb_width * scale_mod[4])); } xmb_draw_thumbnail(video_info, From 0777a6d62581a48e1cb88da259687de04d806176 Mon Sep 17 00:00:00 2001 From: alfrix Date: Thu, 29 Mar 2018 13:45:36 -0300 Subject: [PATCH 056/517] Do not draw the thumbnail if there is no space available --- menu/drivers/xmb.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 97de4fd4f8..140649372c 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3108,6 +3108,14 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) menu_display_rotate_z(&rotate_draw, video_info); menu_display_blend_begin(video_info); + /* Do not draw the right thumbnail if there is no space available */ + + const int min_thumb_size = 50; + + if (((xmb->margins_screen_top + xmb->icon_size + min_thumb_size) <= height) && + ((xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + + xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4 + min_thumb_size) <= width)) + { if (xmb->savestate_thumbnail) xmb_draw_thumbnail(video_info, xmb, &coord_white[0], width, height, @@ -3168,7 +3176,12 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb->thumbnail); } + } + /* Do not draw the left thumbnail if there is no space available */ + + if ((xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) <= window_height) + { /* Left Thumbnail */ if (xmb->left_thumbnail @@ -3204,7 +3217,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) left_thumb_width, left_thumb_height, xmb->left_thumbnail); } - + } /* Clock image */ menu_display_set_alpha(coord_white, MIN(xmb->alpha, 1.00f)); From 647abd6aba98d65d9226cdf377e7350cfa6c34ea Mon Sep 17 00:00:00 2001 From: alfrix Date: Thu, 29 Mar 2018 14:07:22 -0300 Subject: [PATCH 057/517] Buildfix --- menu/drivers/xmb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 140649372c..9d1cadb04d 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3037,6 +3037,8 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) bool render_background = false; file_list_t *selection_buf = NULL; xmb_handle_t *xmb = (xmb_handle_t*)data; + float window_width = video_info->width; + float window_height = video_info->height; if (!xmb) return; @@ -3138,8 +3140,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) thumb_width = xmb->thumbnail_width; thumb_height = xmb->thumbnail_height; - float window_width = video_info->width; - float window_height = video_info->height; const float around_thumb_margin = 0.96; if( xmb->margins_screen_top + xmb->icon_size + xmb->thumbnail_height * scale_mod[4] >= From 54636ea5f332ae7ae463e049eecd66a37377dc80 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 19:35:26 +0200 Subject: [PATCH 058/517] (OSX PPC) Define HAVE_RUNAHEAD --- pkg/apple/RetroArch_PPC.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/apple/RetroArch_PPC.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_PPC.xcodeproj/project.pbxproj index 88d2fe4f7b..af4c288810 100644 --- a/pkg/apple/RetroArch_PPC.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_PPC.xcodeproj/project.pbxproj @@ -288,6 +288,7 @@ ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "-DMSB_FIRST", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_MINIUPNPC", "-DHAVE_BUILTINMINIUPNPC", @@ -362,6 +363,7 @@ "-DNS_BLOCK_ASSERTIONS=1", "-DNDEBUG", "-DMSB_FIRST", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_MINIUPNPC", "-DHAVE_BUILTINMINIUPNPC", From 1103f4f630825dc58125e38c3a425b8547335276 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 19:43:04 +0200 Subject: [PATCH 059/517] (input_overlay.c) Cleanups --- input/input_overlay.c | 61 ++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/input/input_overlay.c b/input/input_overlay.c index 9eac1e8012..136117e7fe 100644 --- a/input/input_overlay.c +++ b/input/input_overlay.c @@ -537,32 +537,35 @@ void input_overlay_set_visibility(int overlay_idx,enum overlay_visibility vis) int i; input_overlay_t *ol = overlay_ptr; - if(visibility == NULL) + if (!visibility) { - visibility = (enum overlay_visibility *)calloc(MAX_VISIBILITY,sizeof(enum overlay_visibility)); - for(i=0;iiface->set_alpha(ol->iface_data, overlay_idx, 0.0); } static enum overlay_visibility input_overlay_get_visibility(int overlay_idx) { - if(visibility == NULL) return OVERLAY_VISIBILITY_DEFAULT; - if((overlay_idx < 0) || (overlay_idx >= MAX_VISIBILITY)) return OVERLAY_VISIBILITY_DEFAULT; + if (!visibility) + return OVERLAY_VISIBILITY_DEFAULT; + if ((overlay_idx < 0) || (overlay_idx >= MAX_VISIBILITY)) + return OVERLAY_VISIBILITY_DEFAULT; return visibility[overlay_idx]; } static bool input_overlay_is_hidden(int overlay_idx) { - return (input_overlay_get_visibility(overlay_idx) == OVERLAY_VISIBILITY_HIDDEN); + return (input_overlay_get_visibility(overlay_idx) + == OVERLAY_VISIBILITY_HIDDEN); } /** @@ -582,7 +585,7 @@ void input_overlay_set_alpha_mod(input_overlay_t *ol, float mod) for (i = 0; i < ol->active->load_images_size; i++) { - if(input_overlay_is_hidden(i)) + if (input_overlay_is_hidden(i)) ol->iface->set_alpha(ol->iface_data, i, 0.0); else ol->iface->set_alpha(ol->iface_data, i, mod); @@ -749,10 +752,9 @@ void input_poll_overlay(input_overlay_t *ol, float opacity, unsigned analog_dpad break; } - if(settings->bools.input_overlay_show_physical_inputs) - { + if (settings->bools.input_overlay_show_physical_inputs) button_pressed = input_overlay_add_inputs(ol, settings->uints.input_overlay_show_physical_inputs_port, analog_dpad_mode); - } + if (button_pressed || polled) input_overlay_post_poll(ol, opacity); else @@ -836,16 +838,16 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc, if (bank_mask & 1) { /* Light up the button if pressed */ - if(input_state(port, RETRO_DEVICE_JOYPAD, 0, id)) + if (input_state(port, RETRO_DEVICE_JOYPAD, 0, id)) { all_buttons_pressed = true; - desc->updated = true; + desc->updated = true; } else { /*we need ALL of the inputs to be active*/ all_buttons_pressed = false; - desc->updated = false; + desc->updated = false; /*abort*/ return false; @@ -863,28 +865,27 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc, case OVERLAY_TYPE_ANALOG_LEFT: case OVERLAY_TYPE_ANALOG_RIGHT: { - float analog_x, analog_y; - float dx, dy; unsigned int index = (desc->type == OVERLAY_TYPE_ANALOG_RIGHT) ? RETRO_DEVICE_INDEX_ANALOG_RIGHT : RETRO_DEVICE_INDEX_ANALOG_LEFT; - analog_x = input_state(port, RETRO_DEVICE_ANALOG, index, RETRO_DEVICE_ID_ANALOG_X); - analog_y = input_state(port, RETRO_DEVICE_ANALOG, index, RETRO_DEVICE_ID_ANALOG_Y); - dx = (analog_x/0x8000)*(desc->range_x/2); - dy = (analog_y/0x8000)*(desc->range_y/2); + float analog_x = input_state(port, RETRO_DEVICE_ANALOG, index, RETRO_DEVICE_ID_ANALOG_X); + float analog_y = input_state(port, RETRO_DEVICE_ANALOG, index, RETRO_DEVICE_ID_ANALOG_Y); + float dx = (analog_x/0x8000)*(desc->range_x/2); + float dy = (analog_y/0x8000)*(desc->range_y/2); + + desc->delta_x = dx; + desc->delta_y = dy; - desc->delta_x = dx; - desc->delta_y = dy; /*Maybe use some option here instead of 0, only display changes greater than some magnitude. */ - if((dx*dx) > 0 || (dy*dy) > 0) + if ((dx * dx) > 0 || (dy*dy) > 0) return true; } break; case OVERLAY_TYPE_KEYBOARD: - if(input_state(port, RETRO_DEVICE_KEYBOARD, 0, desc->retro_key_idx)) + if (input_state(port, RETRO_DEVICE_KEYBOARD, 0, desc->retro_key_idx)) { desc->updated = true; return true; @@ -905,10 +906,10 @@ static bool input_overlay_add_inputs(input_overlay_t *ol, bool button_pressed = false; input_overlay_state_t *ol_state = &ol->overlay_state; - if(!ol_state) + if (!ol_state) return false; - for(i = 0; i < ol->active->size; i++) + for (i = 0; i < ol->active->size; i++) { overlay_desc_t *desc = &(ol->active->descs[i]); button_pressed |= input_overlay_add_inputs_inner(desc, port, analog_dpad_mode); From d04d1c0214ba9ead7457d8467a4cbd78194c242b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 20:01:34 +0200 Subject: [PATCH 060/517] (Runahead) Fix for mingw as suggested by Dwedit --- runahead/secondary_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 29dda8087c..2a7a2657e0 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -1,4 +1,4 @@ -#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC +#if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB) #include #include From dd392dcdfbffcf0277086a6679d33cce27b36fc3 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Thu, 29 Mar 2018 13:22:20 -0500 Subject: [PATCH 061/517] Remove malloc_zero, replace with calloc Replace free_ptr with FREE macro --- runahead/copy_load_info.c | 14 +++++++------- runahead/dirty_input.c | 2 +- runahead/mem_util.c | 17 +---------------- runahead/mem_util.h | 3 --- runahead/mylist.c | 2 +- runahead/secondary_core.c | 23 ++++++++++++----------- 6 files changed, 22 insertions(+), 39 deletions(-) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c index df3d6c591b..b9b6aa590b 100644 --- a/runahead/copy_load_info.c +++ b/runahead/copy_load_info.c @@ -25,7 +25,7 @@ static struct retro_game_info* clone_retro_game_info(const struct retro_game_info *dest; if (src == NULL) return NULL; - dest = (struct retro_game_info*)malloc_zero( + dest = (struct retro_game_info*)calloc(1, sizeof(struct retro_game_info)); dest->path = strcpy_alloc(src->path); dest->data = memcpy_alloc(src->data, src->size); @@ -55,10 +55,10 @@ static struct string_list* clone_string_list(const struct string_list *src) if (!src) return NULL; - dest = (struct string_list*)malloc_zero(sizeof(struct string_list)); + dest = (struct string_list*)calloc(1, sizeof(struct string_list)); dest->size = src->size; dest->cap = src->cap; - dest->elems = (struct string_list_elem*)malloc_zero(sizeof(struct string_list_elem) * dest->size); + dest->elems = (struct string_list_elem*)calloc(dest->size, sizeof(struct string_list_elem)); for (i = 0; i < src->size; i++) { @@ -117,7 +117,7 @@ static void clone_retro_subsystem_rom_info(struct dest->desc = strcpy_alloc(src->desc); dest->valid_extensions = strcpy_alloc(src->valid_extensions); - memory = (struct retro_subsystem_memory_info*)malloc_zero( + memory = (struct retro_subsystem_memory_info*)calloc(1, dest->num_memory * sizeof(struct retro_subsystem_memory_info)); dest->memory = memory; @@ -152,14 +152,14 @@ static retro_subsystem_info* clone_retro_subsystem_info(struct if (src == NULL) return NULL; - dest = (struct retro_subsystem_info*)malloc_zero( + dest = (struct retro_subsystem_info*)calloc(1, sizeof(struct retro_subsystem_info)); dest->desc = strcpy_alloc(src->desc); dest->ident = strcpy_alloc(src->ident); dest->num_roms = src->num_roms; dest->id = src->id; roms = (struct retro_subsystem_rom_info*) - malloc_zero(src->num_roms * sizeof(struct retro_subsystem_rom_info)); + calloc(src->num_roms, sizeof(struct retro_subsystem_rom_info)); dest->roms = roms; for (i = 0; i < src->num_roms; i++) @@ -194,7 +194,7 @@ static struct retro_ctx_load_content_info if (src == NULL || src->special != NULL) return NULL; /* refuse to deal with the Special field */ - dest = (struct retro_ctx_load_content_info*)malloc_zero( + dest = (struct retro_ctx_load_content_info*)calloc(1, sizeof(struct retro_ctx_load_content_info)); dest->info = clone_retro_game_info(src->info); dest->content = clone_string_list(src->content); diff --git a/runahead/dirty_input.c b/runahead/dirty_input.c index 9d25978359..606f40e892 100644 --- a/runahead/dirty_input.c +++ b/runahead/dirty_input.c @@ -34,7 +34,7 @@ static bool unserialze_hook(const void *buf, size_t size); static void* InputListElementConstructor(void) { const int size = sizeof(InputListElement); - void *ptr = malloc_zero(size); + void *ptr = calloc(1, size); return ptr; } diff --git a/runahead/mem_util.c b/runahead/mem_util.c index a350f14c6a..1657be4484 100644 --- a/runahead/mem_util.c +++ b/runahead/mem_util.c @@ -2,21 +2,6 @@ #include "mem_util.h" -void *malloc_zero(size_t size) -{ - void *ptr = malloc(size); - memset(ptr, 0, size); - return ptr; -} - -void free_ptr(void **data_p) -{ - if (!data_p || !*data_p) - return; - free(*data_p); - *data_p = NULL; -} - void *memcpy_alloc(const void *src, size_t size) { void *result = malloc(size); @@ -44,7 +29,7 @@ char *strcpy_alloc_force(const char *sourceStr) { char *result = strcpy_alloc(sourceStr); if (!result) - result = (char*)malloc_zero(1); + result = (char*)calloc(1, 1); return result; } diff --git a/runahead/mem_util.h b/runahead/mem_util.h index fedcf1ca70..3b5c1a41b6 100644 --- a/runahead/mem_util.h +++ b/runahead/mem_util.h @@ -11,8 +11,6 @@ RETRO_BEGIN_DECLS -void *malloc_zero(size_t size); -void free_ptr(void **data_p); char *strcpy_alloc(const char *sourceStr); char *strcpy_alloc_force(const char *sourceStr); void strcat_alloc(char ** destStr_p, const char *appendStr); @@ -21,4 +19,3 @@ void *memcpy_alloc(const void *src, size_t size); RETRO_END_DECLS #endif - diff --git a/runahead/mylist.c b/runahead/mylist.c index 9402bcdee9..11b3f3d6b4 100644 --- a/runahead/mylist.c +++ b/runahead/mylist.c @@ -91,7 +91,7 @@ void mylist_create(MyList **list_p, int initialCapacity, if (initialCapacity > 0) { - list->data = (void**)malloc_zero(initialCapacity * sizeof(void*)); + list->data = (void**)calloc(initialCapacity, sizeof(void*)); list->capacity = initialCapacity; } else diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 2a7a2657e0..1f3ebb8f1c 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -128,16 +128,16 @@ char* copy_core_to_temp_file(void) goto failed; } - free_ptr((void**)&tempDirectory); - free_ptr((void**)&retroarchTempPath); - free_ptr(&dllFileData); + FREE(tempDirectory); + FREE(retroarchTempPath); + FREE(dllFileData); return tempDllPath; failed: - free_ptr((void**)&tempDirectory); - free_ptr((void**)&retroarchTempPath); - free_ptr((void**)&tempDllPath); - free_ptr((void**)&dllFileData); + FREE(tempDirectory); + FREE(retroarchTempPath); + FREE(tempDllPath); + FREE(dllFileData); return NULL; } @@ -168,7 +168,7 @@ bool write_file_with_random_name(char **tempDllPath, numberValue = numberValue * 214013 + 2531011; number = (numberValue >> 14) % 100000; sprintf(numberBuf, "%05d", number); - free_ptr((void**)tempDllPath); + FREE(*tempDllPath); strcat_alloc(tempDllPath, retroarchTempPath); strcat_alloc(tempDllPath, prefix); strcat_alloc(tempDllPath, numberBuf); @@ -178,7 +178,7 @@ bool write_file_with_random_name(char **tempDllPath, break; } - free_ptr((void**)&ext); + FREE(ext); return true; } @@ -199,7 +199,7 @@ bool secondary_core_create(void) load_content_info->special) return false; - free_ptr((void**)&secondary_library_path); + FREE(secondary_library_path); secondary_library_path = copy_core_to_temp_file(); if (!secondary_library_path) @@ -321,7 +321,7 @@ void secondary_core_destroy(void) dylib_close(secondary_module); secondary_module = NULL; filestream_delete(secondary_library_path); - free_ptr((void**)&secondary_library_path); + FREE(secondary_library_path); } } @@ -339,6 +339,7 @@ void clear_controller_port_map(void) for (port = 0; port < 16; port++) port_map[port] = -1; } + #else #include From 5da1aa0af7cb13b8ba98616e86d99ea726aa3476 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Thu, 29 Mar 2018 15:20:14 -0500 Subject: [PATCH 062/517] Allow compositor disabling on X11 fullscreen through _NET_WM_BYPASS_COMPOSITOR. Note: KWin doesn't bypass compositor on override-redirect windows, so enabling windowed fullscreen is necessary. --- gfx/drivers/xvideo.c | 10 ++++++++++ gfx/drivers_context/x_ctx.c | 9 +++++++++ gfx/drivers_context/xegl_ctx.c | 11 +++++++++++ intl/msg_hash_ar.h | 2 +- intl/msg_hash_chs.h | 2 +- intl/msg_hash_cht.h | 2 +- intl/msg_hash_eo.h | 2 +- intl/msg_hash_ja.h | 2 +- intl/msg_hash_nl.h | 2 +- intl/msg_hash_us.h | 2 +- intl/msg_hash_vn.h | 2 +- 11 files changed, 38 insertions(+), 8 deletions(-) diff --git a/gfx/drivers/xvideo.c b/gfx/drivers/xvideo.c index 8df8f528bb..d516035c45 100644 --- a/gfx/drivers/xvideo.c +++ b/gfx/drivers/xvideo.c @@ -562,6 +562,16 @@ static void *xv_init(const video_info_t *video, XFree(visualinfo); XSetWindowBackground(g_x11_dpy, g_x11_win, 0); + if (video->fullscreen && settings->bools.video_disable_composition) + { + uint32_t value = 1; + Atom cardinal = XInternAtom(g_x11_dpy, "CARDINAL", False); + Atom net_wm_bypass_compositor = XInternAtom(g_x11_dpy, "_NET_WM_BYPASS_COMPOSITOR", False); + + RARCH_LOG("[XVideo]: Requesting compositor bypass.\n"); + XChangeProperty(g_x11_dpy, g_x11_win, net_wm_bypass_compositor, cardinal, 32, PropModeReplace, (const unsigned char*)&value, 1); + } + XMapWindow(g_x11_dpy, g_x11_win); video_driver_get_window_title(title, sizeof(title)); diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index 448f5df631..45e6dd02ac 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -726,6 +726,15 @@ static bool gfx_ctx_x_set_video_mode(void *data, XChangeProperty(g_x11_dpy, g_x11_win, net_wm_icon, cardinal, 32, PropModeReplace, (const unsigned char*)retroarch_icon_data, sizeof(retroarch_icon_data) / sizeof(*retroarch_icon_data)); + if (fullscreen && settings->bools.video_disable_composition) + { + uint32_t value = 1; + Atom net_wm_bypass_compositor = XInternAtom(g_x11_dpy, "_NET_WM_BYPASS_COMPOSITOR", False); + + RARCH_LOG("[GLX]: Requesting compositor bypass.\n"); + XChangeProperty(g_x11_dpy, g_x11_win, net_wm_bypass_compositor, cardinal, 32, PropModeReplace, (const unsigned char*)&value, 1); + } + if (opacity < (unsigned)-1) { Atom net_wm_opacity = XInternAtom(g_x11_dpy, "_NET_WM_WINDOW_OPACITY", False); diff --git a/gfx/drivers_context/xegl_ctx.c b/gfx/drivers_context/xegl_ctx.c index f1e20fd78c..c8dbd5432d 100644 --- a/gfx/drivers_context/xegl_ctx.c +++ b/gfx/drivers_context/xegl_ctx.c @@ -24,6 +24,7 @@ #endif #include "../../frontend/frontend_driver.h" +#include "../../configuration.h" #include "../common/egl_common.h" #include "../common/gl_common.h" @@ -342,6 +343,16 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, (true_full ? CWOverrideRedirect : 0), &swa); XSetWindowBackground(g_x11_dpy, g_x11_win, 0); + if (fullscreen && config_get_ptr()->bools.video_disable_composition) + { + uint32_t value = 1; + Atom cardinal = XInternAtom(g_x11_dpy, "CARDINAL", False); + Atom net_wm_bypass_compositor = XInternAtom(g_x11_dpy, "_NET_WM_BYPASS_COMPOSITOR", False); + + RARCH_LOG("[X/EGL]: Requesting compositor bypass.\n"); + XChangeProperty(g_x11_dpy, g_x11_win, net_wm_bypass_compositor, cardinal, 32, PropModeReplace, (const unsigned char*)&value, 1); + } + if (!egl_create_context(&xegl->egl, (attr != egl_attribs) ? egl_attribs : NULL)) { egl_report_error(); diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index bc8f846966..49a95bd27c 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -2565,7 +2565,7 @@ MSG_HASH(MSG_NETPLAY_LAN_SCANNING, MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, "Pause gameplay when RetroArch is not the active window.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Enable or disable composition (Windows only).") + "Enable or disable composition.") MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, "Enable or disable recent playlist for games, images, music, and videos.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index 73c5ac25af..9ac1f14fc7 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -2407,7 +2407,7 @@ MSG_HASH(MSG_NETPLAY_LAN_SCANNING, MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, "当窗口失去焦点时暂停游戏。") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Enable or disable composition (Windows only).") + "Enable or disable composition.") MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, "为游戏、图片、音乐和视频启用/禁用历史记录。") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index fea49f44f3..b54635d9a0 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -2399,7 +2399,7 @@ MSG_HASH(MSG_NETPLAY_LAN_SCANNING, MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, "當窗口失去焦點時暫停遊戲。") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Enable or disable composition (Windows only).") + "Enable or disable composition.") MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, "為遊戲、圖片、音樂和視訊啟用/禁用歷史記錄。") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 2914287c26..89dceddae4 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -2286,7 +2286,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_DATABASE_INFO, MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, "Pause gameplay when window focus is lost.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Enable or disable composition (Windows only).") + "Enable or disable composition.") MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, "Enable or disable recent playlist for games, images, music, and videos.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index 42c28851b2..4bb835f10e 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -2525,7 +2525,7 @@ MSG_HASH(MSG_NETPLAY_LAN_SCANNING, MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, "Pause gameplay when window focus is lost.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Enable or disable composition (Windows only).") + "Enable or disable composition.") MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, "Enable or disable recent playlist for games, images, music, and videos.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index 90eb92fd77..bcb12f83ac 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -2288,7 +2288,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_DATABASE_INFO, MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, "Pause gameplay when window focus is lost.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Enable or disable composition (Windows only).") + "Enable or disable composition.") MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, "Enable or disable recent playlist for games, images, music, and videos.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 9ac3a8728a..5ce8ae6e58 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -2585,7 +2585,7 @@ MSG_HASH(MSG_NETPLAY_LAN_SCANNING, MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, "Pause gameplay when RetroArch is not the active window.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Enable or disable composition (Windows only).") + "Enable or disable composition.") MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, "Enable or disable recent playlist for games, images, music, and videos.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index 1597d8109c..0fee79bf3a 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -2433,7 +2433,7 @@ MSG_HASH(MSG_NETPLAY_LAN_SCANNING, MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, "Pause gameplay when RetroArch is not the active window.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Enable or disable composition (Windows only).") + "Enable or disable composition.") MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, "Enable or disable recent playlist for games, images, music, and videos.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, From ef039c2c083f1d561e13a87f885c00347c5eb3d3 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 29 Mar 2018 22:39:27 +0200 Subject: [PATCH 063/517] Cleanups --- gfx/video_driver.c | 3 ++- runahead/secondary_core.c | 11 ----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/gfx/video_driver.c b/gfx/video_driver.c index e7f8ed667b..30fc5756c5 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -2521,12 +2521,13 @@ void video_driver_frame(const void *data, unsigned width, audio_statistics_t audio_stats = {0.0f}; double stddev = 0.0; struct retro_system_av_info *av_info = &video_driver_av_info; - bool measure_frame_time = video_monitor_fps_statistics(NULL, &stddev, NULL); unsigned red = 255; unsigned green = 255; unsigned blue = 255; unsigned alpha = 255; + video_monitor_fps_statistics(NULL, &stddev, NULL); + video_info.osd_stat_params.x = 0.010f; video_info.osd_stat_params.y = 0.950f; video_info.osd_stat_params.scale = 1.0f; diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 1f3ebb8f1c..9aba8323ba 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -40,10 +40,6 @@ static char* copy_core_to_temp_file(void); static bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, ssize_t dataSize); -static void* InputListElementConstructor(void); - -static void secondary_core_clear(void); - static bool secondary_core_create(void); bool secondary_core_run_no_input_polling(void); @@ -182,13 +178,6 @@ bool write_file_with_random_name(char **tempDllPath, return true; } -void secondary_core_clear(void) -{ - secondary_library_path = NULL; - secondary_module = NULL; - memset(&secondary_core, 0, sizeof(struct retro_core_t)); -} - bool secondary_core_create(void) { long port, device; From 3bbff3c689926542d37eafc92c7ca6a12858de2c Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Thu, 29 Mar 2018 17:11:32 -0500 Subject: [PATCH 064/517] Use _NET_WM_STATE_FULLSCREEN as a first measure, only falling back to override-redirect when not supported. --- gfx/common/x11_common.c | 43 +++++++++++++++++++++++++++++++++- gfx/common/x11_common.h | 3 ++- gfx/drivers/xvideo.c | 2 +- gfx/drivers_context/x_ctx.c | 10 +++++--- gfx/drivers_context/xegl_ctx.c | 9 ++++--- 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index fd2832536b..b1eb6d2886 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -107,7 +107,48 @@ void x11_show_mouse(Display *dpy, Window win, bool state) x11_hide_mouse(dpy, win); } -void x11_windowed_fullscreen(Display *dpy, Window win) +static bool x11_check_atom_supported(Display *dpy, Atom atom) +{ + Atom XA_NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", True); + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + Atom *prop; + int i; + + if (XA_NET_SUPPORTED == None) + return false; + + XGetWindowProperty(dpy, DefaultRootWindow(dpy), XA_NET_SUPPORTED, 0, UINT_MAX, False, XA_ATOM, &type, &format,&nitems, &bytes_after, (unsigned char **) &prop); + + if (!prop || type != XA_ATOM) + { + return false; + } + + for (i = 0; i < nitems; i++) + { + if (prop[i] == atom) + { + XFree(prop); + return true; + } + } + + XFree(prop); + + return false; +} + +bool x11_has_net_wm_fullscreen(Display *dpy) +{ + XA_NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + + return x11_check_atom_supported(dpy, XA_NET_WM_STATE_FULLSCREEN); +} + +void x11_set_net_wm_fullscreen(Display *dpy, Window win) { XEvent xev = {0}; diff --git a/gfx/common/x11_common.h b/gfx/common/x11_common.h index 23fc4898bc..54718b8ce8 100644 --- a/gfx/common/x11_common.h +++ b/gfx/common/x11_common.h @@ -29,7 +29,8 @@ extern Colormap g_x11_cmap; extern unsigned g_x11_screen; void x11_show_mouse(Display *dpy, Window win, bool state); -void x11_windowed_fullscreen(Display *dpy, Window win); +bool x11_has_net_wm_fullscreen(Display *dpy); +void x11_set_net_wm_fullscreen(Display *dpy, Window win); void x11_suspend_screensaver(Window win, bool enable); bool x11_enter_fullscreen(video_frame_info_t *video_info, Display *dpy, unsigned width, diff --git a/gfx/drivers/xvideo.c b/gfx/drivers/xvideo.c index d516035c45..0479fa73bb 100644 --- a/gfx/drivers/xvideo.c +++ b/gfx/drivers/xvideo.c @@ -583,7 +583,7 @@ static void *xv_init(const video_info_t *video, if (video->fullscreen) { - x11_windowed_fullscreen(g_x11_dpy, g_x11_win); + x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); x11_show_mouse(g_x11_dpy, g_x11_win, false); } diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index 45e6dd02ac..eec1f3dc2b 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -37,6 +37,7 @@ #endif #include +#include #include "../../configuration.h" #include "../../frontend/frontend_driver.h" @@ -678,7 +679,6 @@ static bool gfx_ctx_x_set_video_mode(void *data, swa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | LeaveWindowMask | EnterWindowMask | ButtonReleaseMask | ButtonPressMask; - swa.override_redirect = fullscreen ? True : False; if (fullscreen && !windowed_full) { @@ -691,6 +691,8 @@ static bool gfx_ctx_x_set_video_mode(void *data, RARCH_ERR("[GLX]: Entering true fullscreen failed. Will attempt windowed mode.\n"); } + swa.override_redirect = (!x11_has_net_wm_fullscreen(g_x11_dpy) && true_full) ? True : False; + if (video_info->monitor_index) g_x11_screen = video_info->monitor_index - 1; @@ -721,7 +723,7 @@ static bool gfx_ctx_x_set_video_mode(void *data, x_off, y_off, width, height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask | - (true_full ? CWOverrideRedirect : 0), &swa); + CWOverrideRedirect, &swa); XSetWindowBackground(g_x11_dpy, g_x11_win, 0); XChangeProperty(g_x11_dpy, g_x11_win, net_wm_icon, cardinal, 32, PropModeReplace, (const unsigned char*)retroarch_icon_data, sizeof(retroarch_icon_data) / sizeof(*retroarch_icon_data)); @@ -778,6 +780,8 @@ static bool gfx_ctx_x_set_video_mode(void *data, { RARCH_LOG("[GLX]: Using true fullscreen.\n"); XMapRaised(g_x11_dpy, g_x11_win); + if (swa.override_redirect == False) + x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); } else if (fullscreen) { @@ -792,7 +796,7 @@ static bool gfx_ctx_x_set_video_mode(void *data, * x_off and y_off usually get ignored in XCreateWindow(). */ x11_move_window(g_x11_dpy, g_x11_win, x_off, y_off, width, height); - x11_windowed_fullscreen(g_x11_dpy, g_x11_win); + x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); } else { diff --git a/gfx/drivers_context/xegl_ctx.c b/gfx/drivers_context/xegl_ctx.c index c8dbd5432d..e791bf1f42 100644 --- a/gfx/drivers_context/xegl_ctx.c +++ b/gfx/drivers_context/xegl_ctx.c @@ -297,7 +297,6 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, vi->visual, AllocNone); swa.event_mask = StructureNotifyMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | KeyReleaseMask; - swa.override_redirect = fullscreen ? True : False; if (fullscreen && !video_info->windowed_fullscreen) { @@ -310,6 +309,8 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, RARCH_ERR("[X/EGL]: Entering true fullscreen failed. Will attempt windowed mode.\n"); } + swa.override_redirect = (!x11_has_net_wm_fullscreen(g_x11_dpy) && true_full) ? True : False; + if (video_info->monitor_index) g_x11_screen = video_info->monitor_index - 1; @@ -340,7 +341,7 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, x_off, y_off, width, height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask | - (true_full ? CWOverrideRedirect : 0), &swa); + CWOverrideRedirect, &swa); XSetWindowBackground(g_x11_dpy, g_x11_win, 0); if (fullscreen && config_get_ptr()->bools.video_disable_composition) @@ -372,6 +373,8 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, { RARCH_LOG("[X/EGL]: Using true fullscreen.\n"); XMapRaised(g_x11_dpy, g_x11_win); + if (swa.override_redirect == False) + x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); } else if (fullscreen) { @@ -385,7 +388,7 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, * x_off and y_off usually get ignored in XCreateWindow(). */ x11_move_window(g_x11_dpy, g_x11_win, x_off, y_off, width, height); - x11_windowed_fullscreen(g_x11_dpy, g_x11_win); + x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); } else { From 008900e73d2541cfe2fa0cb2488d829549f2b7ac Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 30 Mar 2018 02:30:56 +0200 Subject: [PATCH 065/517] (D3D11/D3D12) Fix crashes with completely black or white thumbnail textures in XMB --- gfx/common/d3d11_common.c | 3 +++ gfx/common/d3d12_common.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/gfx/common/d3d11_common.c b/gfx/common/d3d11_common.c index a815f04538..7ae98d995d 100644 --- a/gfx/common/d3d11_common.c +++ b/gfx/common/d3d11_common.c @@ -137,6 +137,9 @@ void d3d11_update_texture( D3D11_MAPPED_SUBRESOURCE mapped_texture; D3D11_BOX frame_box = { 0, 0, 0, width, height, 1 }; + if (!texture || !texture->staging) + return; + D3D11MapTexture2D(ctx, texture->staging, 0, D3D11_MAP_WRITE, 0, &mapped_texture); dxgi_copy( diff --git a/gfx/common/d3d12_common.c b/gfx/common/d3d12_common.c index b6143c4cf9..163fd676f5 100644 --- a/gfx/common/d3d12_common.c +++ b/gfx/common/d3d12_common.c @@ -733,6 +733,9 @@ void d3d12_update_texture( uint8_t* dst; D3D12_RANGE read_range = { 0, 0 }; + if (!texture || !texture->upload_buffer) + return; + D3D12Map(texture->upload_buffer, 0, &read_range, (void**)&dst); dxgi_copy( From d8787ef805d939c3e2d4f0814cab05748c50228b Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Fri, 30 Mar 2018 02:37:07 +0200 Subject: [PATCH 066/517] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index fbc6cd11a9..a1f429c925 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ - CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. +- D3D11/D3D12: Fix crashes with completely black or white thumbnail textures in XMB. - LIBRETRO: Addition - Functions to enable and disable audio and video, and an environment function to query status of audio and video enables. - LOCALIZATION: Update Italian translation. - LOCALIZATION: Update Polish translation. From e8a77e6b91c853868987f475bcf73f24ea73c9b6 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Fri, 30 Mar 2018 02:41:09 +0200 Subject: [PATCH 067/517] Update CHANGES.md --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a1f429c925..bfac76d40f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,8 @@ - WINDOWS XP: Add Cheevos support. - WINDOWS/MSVC 2005: Add Cheevos support. - VITA: Bugfix for 'PS Vita takes many time to start to accept input' issue. +- X11: Allow compositor disabling on X11 fullscreen through _NET_WM_BYPASS_COMPOSITOR +- X11: Prioritize _NET_WM_STATE_FULLSCREEN in true fullscreen mode - WIIU: Fix OOB read/write in keyboard driver. # 1.7.1 From 33d9eee43689139c747dd43e5306dee20e3a7a60 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Fri, 30 Mar 2018 02:41:40 +0200 Subject: [PATCH 068/517] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index bfac76d40f..a28e61b67b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -29,7 +29,7 @@ - VULKAN/X11: Fix X11 Vulkan bug from Wayland driver. - VULKAN: Fix multi-line text spacing in menus with Vulkan driver. - WINDOWS XP: Add Cheevos support. -- WINDOWS/MSVC 2005: Add Cheevos support. +- WINDOWS/MSVC 2003/2005/2010/2013/2015/2017: Add Cheevos support. - VITA: Bugfix for 'PS Vita takes many time to start to accept input' issue. - X11: Allow compositor disabling on X11 fullscreen through _NET_WM_BYPASS_COMPOSITOR - X11: Prioritize _NET_WM_STATE_FULLSCREEN in true fullscreen mode From 12bf69fa293740335f069cbdba5878ce14cfd82b Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Fri, 30 Mar 2018 02:44:06 +0200 Subject: [PATCH 069/517] Update CHANGES.md --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index a28e61b67b..eedce75c51 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,11 +10,13 @@ - LOCALIZATION: Update Italian translation. - LOCALIZATION: Update Polish translation. - MENU: Disable XMB shadow icons by default for PowerPC and ARM for performance reasons. -- MENU/XMB: Add Left Thumbnails (additional to the right). +- MENU/XMB: Left/right thumbnails are now automatically scaled according to layout. +- MENU/XMB: Add Left Thumbnails (additional to the right). - MENU/XMB: Fixed left/right tab regression. - MENU/XMB: Fix scaling of tall images that were cut on bottom previously. - MENU/XMB: Menu scale factor setting now changes texts length, image scaling and margins. - MENU/XMB: Mouse cursor scales correctly now. +- MENU/MaterialUI: Automatic DPI Scaling should be much improved now, now scales as expected at 1440p and 4K resolutions. - MENU/MaterialUI: Fix wrong calculation of an entry height causing long playlists to end up outside of screen range. This also could cause crashes on low DPI screens. - IOS: Fixed crash when opening downloaded roms from Safari or using the "Open in.." functionality. Added the compiler flag to support keyboard remapping to controls. - IOS: Fixed buffer overlap that caused a crash while trying to download GLSL shaders from the buildbot. From 58df2e6427de5143520d7e60b583b23dfd5c075c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 30 Mar 2018 02:50:18 +0200 Subject: [PATCH 070/517] update --- menu/drivers/xmb.c | 65 +++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 9d1cadb04d..1bc389aae9 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3031,6 +3031,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) char msg[1024]; char title_msg[255]; char title_truncated[255]; + const int min_thumb_size = 50; settings_t *settings = config_get_ptr(); unsigned width = video_info->width; unsigned height = video_info->height; @@ -3112,8 +3113,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Do not draw the right thumbnail if there is no space available */ - const int min_thumb_size = 50; - if (((xmb->margins_screen_top + xmb->icon_size + min_thumb_size) <= height) && ((xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4 + min_thumb_size) <= width)) @@ -3182,41 +3181,41 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) if ((xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) <= window_height) { - /* Left Thumbnail */ + /* Left Thumbnail */ - if (xmb->left_thumbnail - && !string_is_equal(xmb_thumbnails_ident('L'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - float scale_factor = (settings->uints.menu_xmb_scale_factor * width) / (1920.0 * 100); - - /* Limit left thumbnail height to screen height + margin. */ - if (xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + - xmb->left_thumbnail_height >= (float)(height - (96.0 * scale_factor))) + if (xmb->left_thumbnail + && !string_is_equal(xmb_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { - left_thumb_width = xmb->left_thumbnail_width * - (((float)(height - (96.0 * scale_factor)) - xmb->margins_screen_top - - (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / - xmb->left_thumbnail_height); + float scale_factor = (settings->uints.menu_xmb_scale_factor * width) / (1920.0 * 100); - left_thumb_height = xmb->left_thumbnail_height * - (((float)(height - (96.0 * scale_factor)) - xmb->margins_screen_top - - (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / - xmb->left_thumbnail_height); - } - else - { - left_thumb_width = xmb->left_thumbnail_width; - left_thumb_height = xmb->left_thumbnail_height; - } + /* Limit left thumbnail height to screen height + margin. */ + if (xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + + xmb->left_thumbnail_height >= (float)(height - (96.0 * scale_factor))) + { + left_thumb_width = xmb->left_thumbnail_width * + (((float)(height - (96.0 * scale_factor)) - xmb->margins_screen_top - + (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / + xmb->left_thumbnail_height); - xmb_draw_thumbnail(video_info, - xmb, &coord_white[0], width, height, - 20 * scale_factor + ((xmb->left_thumbnail_width - left_thumb_width) / 2), - xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + left_thumb_height, - left_thumb_width, left_thumb_height, - xmb->left_thumbnail); - } + left_thumb_height = xmb->left_thumbnail_height * + (((float)(height - (96.0 * scale_factor)) - xmb->margins_screen_top - + (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / + xmb->left_thumbnail_height); + } + else + { + left_thumb_width = xmb->left_thumbnail_width; + left_thumb_height = xmb->left_thumbnail_height; + } + + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + 20 * scale_factor + ((xmb->left_thumbnail_width - left_thumb_width) / 2), + xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + left_thumb_height, + left_thumb_width, left_thumb_height, + xmb->left_thumbnail); + } } /* Clock image */ menu_display_set_alpha(coord_white, MIN(xmb->alpha, 1.00f)); From 50f19630bdef2d498065fc0423c7391c03527036 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Fri, 30 Mar 2018 03:00:58 +0200 Subject: [PATCH 071/517] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index eedce75c51..e14b27a8ae 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,7 @@ # 1.7.2 (future) - ANDROID/OPENSL: Prevent crashes when setting audio latency too low (buffer count can never be lower than 2 now). - COMMON: Add way to reset core association for playlist entry. +- COMMON: Fix invalid long command line options causing infinite loop on Windows - COMMON: Add OSD statistics for video/audio/core. - CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. From 05e34654c63499cdc69318e07a19ff8f2b321a80 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Fri, 30 Mar 2018 03:03:03 +0200 Subject: [PATCH 072/517] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index e14b27a8ae..b14883ca2a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ - COMMON: Add way to reset core association for playlist entry. - COMMON: Fix invalid long command line options causing infinite loop on Windows - COMMON: Add OSD statistics for video/audio/core. +- COMMON: Added runahead system; allows you to drive down latency even further. - CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. From ecd42f1aa8144605c0b172ccf333741433986703 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 29 Mar 2018 22:15:16 -0400 Subject: [PATCH 073/517] C89 buildfix, correct spacing --- menu/drivers/xmb.c | 112 ++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 1bc389aae9..83e0a598e3 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3040,6 +3040,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb_handle_t *xmb = (xmb_handle_t*)data; float window_width = video_info->width; float window_height = video_info->height; + const float around_thumb_margin = 0.96; if (!xmb) return; @@ -3073,6 +3074,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) selection = menu_navigation_get_selection(); strlcpy(title_truncated, xmb->title_name, sizeof(title_truncated)); + if (selection > 1) { /* skip 25 utf8 multi-byte chars */ @@ -3117,64 +3119,62 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) ((xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4 + min_thumb_size) <= width)) { - if (xmb->savestate_thumbnail) - xmb_draw_thumbnail(video_info, - xmb, &coord_white[0], width, height, - xmb->margins_screen_left * scale_mod[5] - + xmb->icon_spacing_horizontal + - xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4, - xmb->margins_screen_top + xmb->icon_size + xmb->savestate_thumbnail_height * scale_mod[4], - xmb->savestate_thumbnail_width, xmb->savestate_thumbnail_height * scale_mod[4], - xmb->savestate_thumbnail); - else if (xmb->thumbnail - && !string_is_equal(xmb_thumbnails_ident('R'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { -#ifdef XMB_DEBUG - RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", xmb->thumbnail_width, xmb->thumbnail_height); - RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); -#endif - - /* Limit thumbnail height to screen height + margin. */ - - thumb_width = xmb->thumbnail_width; - thumb_height = xmb->thumbnail_height; - const float around_thumb_margin = 0.96; - - if( xmb->margins_screen_top + xmb->icon_size + xmb->thumbnail_height * scale_mod[4] >= - (window_height * around_thumb_margin) ) + if (xmb->savestate_thumbnail) + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + + xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4, + xmb->margins_screen_top + xmb->icon_size + xmb->savestate_thumbnail_height * scale_mod[4], + xmb->savestate_thumbnail_width, xmb->savestate_thumbnail_height * scale_mod[4], + xmb->savestate_thumbnail); + else if (xmb->thumbnail + && !string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { - thumb_width = thumb_width * - (((window_height * around_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / - (thumb_height * scale_mod[4])); - thumb_height = thumb_height * - (((window_height * around_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / - (thumb_height * scale_mod[4])); + #ifdef XMB_DEBUG + RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", xmb->thumbnail_width, xmb->thumbnail_height); + RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + /* Limit thumbnail height to screen height + margin. */ + + thumb_width = xmb->thumbnail_width; + thumb_height = xmb->thumbnail_height; + + if (xmb->margins_screen_top + xmb->icon_size + xmb->thumbnail_height * scale_mod[4] >= + (window_height * around_thumb_margin)) + { + thumb_width = thumb_width * + (((window_height * around_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / + (thumb_height * scale_mod[4])); + thumb_height = thumb_height * + (((window_height * around_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / + (thumb_height * scale_mod[4])); + } + + /* Limit thumbnail width */ + + if (xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + + xmb->icon_spacing_horizontal*4 - xmb->icon_size / 4 + thumb_width * scale_mod[4] >= + (window_width * around_thumb_margin)) + { + thumb_height = thumb_height * + (((window_width * around_thumb_margin) - xmb->margins_screen_left * scale_mod[5] - xmb->icon_spacing_horizontal - + xmb->icon_spacing_horizontal * 4 + xmb->icon_size / 4) / (thumb_width * scale_mod[4])); + thumb_width = thumb_width * + (((window_width * around_thumb_margin) - xmb->margins_screen_left * scale_mod[5] - xmb->icon_spacing_horizontal - + xmb->icon_spacing_horizontal * 4 + xmb->icon_size / 4) / (thumb_width * scale_mod[4])); + } + + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + + xmb->icon_spacing_horizontal*4 - xmb->icon_size / 4, + xmb->margins_screen_top + xmb->icon_size + thumb_height * scale_mod[4], + thumb_width * scale_mod[4], thumb_height * scale_mod[4], + xmb->thumbnail); } - - /* Limit thumbnail width */ - - if ( xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + - xmb->icon_spacing_horizontal*4 - xmb->icon_size / 4 + thumb_width * scale_mod[4] >= - (window_width * around_thumb_margin) ) - { - thumb_height = thumb_height * - (((window_width * around_thumb_margin) - xmb->margins_screen_left * scale_mod[5] - xmb->icon_spacing_horizontal - - xmb->icon_spacing_horizontal * 4 + xmb->icon_size / 4) / (thumb_width * scale_mod[4])); - thumb_width = thumb_width * - (((window_width * around_thumb_margin) - xmb->margins_screen_left * scale_mod[5] - xmb->icon_spacing_horizontal - - xmb->icon_spacing_horizontal * 4 + xmb->icon_size / 4) / (thumb_width * scale_mod[4])); - } - - xmb_draw_thumbnail(video_info, - xmb, &coord_white[0], width, height, - xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + - xmb->icon_spacing_horizontal*4 - xmb->icon_size / 4, - xmb->margins_screen_top + xmb->icon_size + thumb_height * scale_mod[4], - thumb_width * scale_mod[4], thumb_height * scale_mod[4], - xmb->thumbnail); - - } } /* Do not draw the left thumbnail if there is no space available */ From 7e43db9cf1a5340fd534185bb4362fb82cf95fb0 Mon Sep 17 00:00:00 2001 From: i30817 Date: Fri, 30 Mar 2018 03:58:42 +0100 Subject: [PATCH 074/517] fix scanner skipping directories with dots --- libretro-common/lists/dir_list.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libretro-common/lists/dir_list.c b/libretro-common/lists/dir_list.c index 73148c83e3..bcd884a177 100644 --- a/libretro-common/lists/dir_list.c +++ b/libretro-common/lists/dir_list.c @@ -185,13 +185,16 @@ static int dir_list_read(const char *dir, bool is_dir = false; int ret = 0; const char *name = retro_dirent_get_name(entry); - const char *file_ext = path_get_extension(name); + const char *file_ext = ""; file_path[0] = '\0'; fill_pathname_join(file_path, dir, name, sizeof(file_path)); is_dir = retro_dirent_is_dir(entry, file_path); + if(!is_dir) + file_ext = path_get_extension(name); + if (!include_hidden) { if (*name == '.') @@ -200,7 +203,7 @@ static int dir_list_read(const char *dir, if(is_dir && recursive) { - if(strstr(name, ".") || strstr(name, "..")) + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; dir_list_read(file_path, list, ext_list, include_dirs, From ae19eed00f0aaff0bea6af7ed9d4c6fe1f50cf67 Mon Sep 17 00:00:00 2001 From: gblues Date: Mon, 5 Feb 2018 23:21:00 -0800 Subject: [PATCH 075/517] implement hid device search --- Makefile.wiiu | 8 +++++-- input/common/hid/device_ds3.c | 26 ++++++++++++++++++++ input/common/hid/device_ds4.c | 26 ++++++++++++++++++++ input/common/hid/device_wiiu_gca.c | 25 +++++++++++++++++++ input/common/hid/hid_device_driver.c | 36 ++++++++++++++++++++++++++++ input/common/hid/hid_device_driver.h | 32 +++++++++++++++++++++++++ wiiu/include/wiiu/pad_driver.h | 2 ++ wiiu/input/wiiu_hid.c | 8 +++++++ 8 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 input/common/hid/device_ds3.c create mode 100644 input/common/hid/device_ds4.c create mode 100644 input/common/hid/device_wiiu_gca.c create mode 100644 input/common/hid/hid_device_driver.c create mode 100644 input/common/hid/hid_device_driver.h diff --git a/Makefile.wiiu b/Makefile.wiiu index 4b5eeafc02..798028e5de 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -6,7 +6,7 @@ DEBUG = 0 GRIFFIN_BUILD = 0 SALAMANDER_BUILD = 0 WHOLE_ARCHIVE_LINK = 0 -WIIU_HID = 0 +WIIU_HID = 1 WIIU_LOG_RPX = 0 BUILD_DIR = objs/wiiu PC_DEVELOPMENT_IP_ADDRESS ?= @@ -56,7 +56,11 @@ ifeq ($(WIIU_HID),1) input/connect/connect_nesusb.o \ input/connect/connect_snesusb.o \ input/connect/connect_wiiupro.o \ - input/connect/connect_wiiugca.o + input/connect/connect_wiiugca.o \ + input/common/hid/hid_device_driver.o \ + input/common/hid/device_wiiu_gca.o \ + input/common/hid/device_ds3.o \ + input/common/hid/device_ds4.o endif ifeq ($(SALAMANDER_BUILD),1) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c new file mode 100644 index 0000000000..04325ace7b --- /dev/null +++ b/input/common/hid/device_ds3.c @@ -0,0 +1,26 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "hid_device_driver.h" + +static bool ds3_detect(uint16_t vendor_id, uint16_t product_id) +{ + return false; +} + +hid_device_t ds3_hid_device = { + ds3_detect +}; diff --git a/input/common/hid/device_ds4.c b/input/common/hid/device_ds4.c new file mode 100644 index 0000000000..0f049e5cbc --- /dev/null +++ b/input/common/hid/device_ds4.c @@ -0,0 +1,26 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "hid_device_driver.h" + +static bool ds4_detect(uint16_t vendor_id, uint16_t product_id) +{ + return false; +} + +hid_device_t ds4_hid_device = { + ds4_detect +}; diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c new file mode 100644 index 0000000000..0829a22ac1 --- /dev/null +++ b/input/common/hid/device_wiiu_gca.c @@ -0,0 +1,25 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "hid_device_driver.h" + +static bool wiiu_gca_detect(uint16_t vendor_id, uint16_t product_id) { + return false; +} + +hid_device_t wiiu_gca_hid_device = { + wiiu_gca_detect +}; diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c new file mode 100644 index 0000000000..0f049ca728 --- /dev/null +++ b/input/common/hid/hid_device_driver.c @@ -0,0 +1,36 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "hid_device_driver.h" + +hid_device_t *hid_device_list[] = { + &wiiu_gca_hid_device, + &ds3_hid_device, + &ds4_hid_device, + NULL /* must be last entry in list */ +}; + +hid_device_t *hid_device_driver_lookup(uint16_t vendor_id, uint16_t product_id) { + int i = 0; + + for(i = 0; hid_device_list[i] != NULL; i++) { + if(hid_device_list[i]->detect(vendor_id, product_id)) + return hid_device_list[i]; + } + + return NULL; +} + diff --git a/input/common/hid/hid_device_driver.h b/input/common/hid/hid_device_driver.h new file mode 100644 index 0000000000..90e0659026 --- /dev/null +++ b/input/common/hid/hid_device_driver.h @@ -0,0 +1,32 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef HID_DEVICE_DRIVER__H +#define HID_DEVICE_DRIVER__H + +#include "../../input_driver.h" + +typedef struct hid_device { + bool (*detect)(uint16_t vid, uint16_t pid); +} hid_device_t; + +extern hid_device_t wiiu_gca_hid_device; +extern hid_device_t ds3_hid_device; +extern hid_device_t ds4_hid_device; + +hid_device_t *hid_device_driver_lookup(uint16_t vendor_id, uint16_t product_id); + +#endif /* HID_DEVICE_DRIVER__H */ diff --git a/wiiu/include/wiiu/pad_driver.h b/wiiu/include/wiiu/pad_driver.h index 376b899f43..8a93798de7 100644 --- a/wiiu/include/wiiu/pad_driver.h +++ b/wiiu/include/wiiu/pad_driver.h @@ -31,6 +31,7 @@ #include #include "../../input/input_driver.h" +#include "../../input/common/hid/hid_device_driver.h" #include "../../input/connect/joypad_connection.h" #include "../../tasks/tasks_internal.h" #include "../../retroarch.h" @@ -149,6 +150,7 @@ typedef struct wiiu_attach wiiu_attach_event; struct wiiu_attach { wiiu_attach_event *next; + hid_device_t *driver; uint32_t type; uint32_t handle; uint16_t vendor_id; diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index db0fcd074b..43eb372752 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -649,10 +649,18 @@ static void delete_adapter(wiiu_adapter_t *adapter) static wiiu_attach_event *new_attach_event(HIDDevice *device) { + hid_device_t *driver = hid_device_driver_lookup(device->vid, device->pid); + if(!driver) + { + RARCH_ERR("[hid]: Failed to locate driver for device vid=%04x pid=%04x\n", + device->vid, device->pid); + return NULL; + } wiiu_attach_event *event = alloc_zeroed(4, sizeof(wiiu_attach_event)); if(!event) return NULL; + event->driver = driver; event->handle = device->handle; event->vendor_id = device->vid; event->product_id = device->pid; From 41ce8853d7bb7a40e0e064e285c0542460bf5f22 Mon Sep 17 00:00:00 2001 From: gblues Date: Wed, 7 Feb 2018 22:28:25 -0800 Subject: [PATCH 076/517] Add name for hid device; implement detect == DETAILS - detect() methods in device_* files now check for VID/PID instead of just returning false - add "name" field on hid device, mainly for logging purposes == TESTING Verified my WiiU GC adapter detected properly --- input/common/hid/device_ds3.c | 5 +++-- input/common/hid/device_ds4.c | 5 +++-- input/common/hid/device_wiiu_gca.c | 5 +++-- input/common/hid/hid_device_driver.h | 2 ++ wiiu/input/wiiu_hid.c | 1 + 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index 04325ace7b..9af43ac1c6 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -18,9 +18,10 @@ static bool ds3_detect(uint16_t vendor_id, uint16_t product_id) { - return false; + return vendor_id == VID_SONY && product_id == PID_SONY_DS3; } hid_device_t ds3_hid_device = { - ds3_detect + ds3_detect, + "Sony DualShock 3" }; diff --git a/input/common/hid/device_ds4.c b/input/common/hid/device_ds4.c index 0f049e5cbc..7c90ebaf0a 100644 --- a/input/common/hid/device_ds4.c +++ b/input/common/hid/device_ds4.c @@ -18,9 +18,10 @@ static bool ds4_detect(uint16_t vendor_id, uint16_t product_id) { - return false; + return vendor_id == VID_SONY && product_id == PID_SONY_DS4; } hid_device_t ds4_hid_device = { - ds4_detect + ds4_detect, + "Sony DualShock 4" }; diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 0829a22ac1..ec68aa897d 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -17,9 +17,10 @@ #include "hid_device_driver.h" static bool wiiu_gca_detect(uint16_t vendor_id, uint16_t product_id) { - return false; + return vendor_id == VID_NINTENDO && product_id == PID_NINTENDO_GCA; } hid_device_t wiiu_gca_hid_device = { - wiiu_gca_detect + wiiu_gca_detect, + "Wii U Gamecube Adapter" }; diff --git a/input/common/hid/hid_device_driver.h b/input/common/hid/hid_device_driver.h index 90e0659026..a3259aee52 100644 --- a/input/common/hid/hid_device_driver.h +++ b/input/common/hid/hid_device_driver.h @@ -18,9 +18,11 @@ #define HID_DEVICE_DRIVER__H #include "../../input_driver.h" +#include "../../connect/joypad_connection.h" typedef struct hid_device { bool (*detect)(uint16_t vid, uint16_t pid); + const char *name; } hid_device_t; extern hid_device_t wiiu_gca_hid_device; diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index 43eb372752..f2656a4cbb 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -656,6 +656,7 @@ static wiiu_attach_event *new_attach_event(HIDDevice *device) device->vid, device->pid); return NULL; } + RARCH_LOG("[hid]: Found HID device driver: %s\n", driver->name); wiiu_attach_event *event = alloc_zeroed(4, sizeof(wiiu_attach_event)); if(!event) return NULL; From 0100d58ffb376315de9060f723a03a76a3f676c6 Mon Sep 17 00:00:00 2001 From: gblues Date: Wed, 21 Feb 2018 22:57:28 -0800 Subject: [PATCH 077/517] WIP: evolve driver implementation == DETAILS I've created the concept of a hid_driver_instance_t which is basically a central place to store the hid pad driver, hid subsystem driver, the pad list, and the instance data for the above in a central location. The HID pad device drivers can use it to perform HID operations in a generic manner. This is more-or-less a pause point so I can catch up with upstream. == TESTING Haven't tested this yet. Compiles without warnings though! --- input/common/hid/device_ds3.c | 26 +++++++ input/common/hid/device_ds4.c | 26 +++++++ input/common/hid/device_wiiu_gca.c | 112 +++++++++++++++++++++++++++ input/common/hid/hid_device_driver.c | 101 ++++++++++++++++++++++++ input/common/hid/hid_device_driver.h | 9 +++ input/connect/connect_ps3.c | 5 +- input/connect/connect_ps4.c | 1 + input/connect/connect_wii.c | 1 + input/connect/connect_wiiugca.c | 1 + input/connect/connect_wiiupro.c | 1 + input/drivers_hid/btstack_hid.c | 7 +- input/drivers_hid/iohidmanager_hid.c | 8 +- input/drivers_hid/libusb_hid.c | 7 +- input/drivers_hid/null_hid.c | 5 +- input/drivers_hid/wiiusb_hid.c | 6 +- input/include/hid_driver.h | 63 +++++++++++++++ input/include/hid_types.h | 24 ++++++ input/input_driver.c | 13 ++++ input/input_driver.h | 32 +++----- wiiu/include/wiiu/pad_driver.h | 9 ++- wiiu/include/wiiu/syshid.h | 9 --- wiiu/input/hidpad_driver.c | 47 ++--------- wiiu/input/kpad_driver.c | 2 +- wiiu/input/pad_functions.c | 8 -- wiiu/input/wiiu_hid.c | 77 ++++++++++-------- wiiu/input/wiiu_hid.h | 1 + wiiu/input/wpad_driver.c | 2 +- 27 files changed, 471 insertions(+), 132 deletions(-) create mode 100644 input/include/hid_driver.h create mode 100644 input/include/hid_types.h diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index 9af43ac1c6..569d4d7f79 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -16,12 +16,38 @@ #include "hid_device_driver.h" +struct ds3_instance { + hid_driver_t *driver; + void *handle; +}; + +static void *ds3_init(hid_driver_instance_t *hid_driver) +{ + return NULL; +} + +static void ds3_free(void *data) +{ + struct ds3_instance *instance = (struct ds3_instance *)data; + if(!instance) + return; + + free(instance); +} + +static void ds3_handle_packet(void *data, uint8_t *buffer, size_t size) +{ +} + static bool ds3_detect(uint16_t vendor_id, uint16_t product_id) { return vendor_id == VID_SONY && product_id == PID_SONY_DS3; } hid_device_t ds3_hid_device = { + ds3_init, + ds3_free, + ds3_handle_packet, ds3_detect, "Sony DualShock 3" }; diff --git a/input/common/hid/device_ds4.c b/input/common/hid/device_ds4.c index 7c90ebaf0a..6c8b77ac2f 100644 --- a/input/common/hid/device_ds4.c +++ b/input/common/hid/device_ds4.c @@ -16,12 +16,38 @@ #include "hid_device_driver.h" +struct ds4_instance { + hid_driver_t *driver; + void *handle; +}; + +static void *ds4_init(hid_driver_instance_t *hid_driver) +{ + return NULL; +} + +static void ds4_free(void *data) +{ + struct ds4_instance *instance = (struct ds4_instance *)data; + if(!instance) + return; + + free(instance); +} + +static void ds4_handle_packet(void *data, uint8_t *buffer, size_t size) +{ +} + static bool ds4_detect(uint16_t vendor_id, uint16_t product_id) { return vendor_id == VID_SONY && product_id == PID_SONY_DS4; } hid_device_t ds4_hid_device = { + ds4_init, + ds4_free, + ds4_handle_packet, ds4_detect, "Sony DualShock 4" }; diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index ec68aa897d..211f67dfbb 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -16,11 +16,123 @@ #include "hid_device_driver.h" +#ifdef WII +static uint8_t activation_packet[] = { 0x01, 0x13 }; +#else +static uint8_t activation_packet[] = { 0x13 }; +#endif + +#define GCA_PORT_INITIALIZING 0x00 +#define GCA_PORT_EMPTY 0x04 +#define GCA_PORT_CONNECTED 0x14 + +typedef struct wiiu_gca_instance { + hid_driver_instance_t *driver; + uint8_t device_state[37]; + joypad_connection_t *pads[4]; +} wiiu_gca_instance_t; + +static void update_pad_state(wiiu_gca_instance_t *instance); +static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance); + +extern pad_connection_interface_t wiiu_gca_pad_connection; + +static void *wiiu_gca_init(hid_driver_instance_t *driver) +{ + wiiu_gca_instance_t *instance = calloc(1, sizeof(wiiu_gca_instance_t)); + memset(instance, 0, sizeof(wiiu_gca_instance_t)); + instance->driver = driver; + + driver->hid_driver->send_control(driver->hid_data, activation_packet, sizeof(activation_packet)); + driver->hid_driver->read(driver->hid_data, instance->device_state, sizeof(instance->device_state)); + + return instance; +} + +static void wiiu_gca_free(void *data) { + wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; + if(instance) { + free(instance); + } +} + +static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size) +{ + wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; + if(!instance) + return; + + if(size > sizeof(instance->device_state)) + return; + + memcpy(instance->device_state, buffer, size); + update_pad_state(instance); +} + +static void update_pad_state(wiiu_gca_instance_t *instance) +{ + int i, pad; + + /* process each pad */ + for(i = 1; i < 37; i += 9) + { + pad = i / 9; + switch(instance->device_state[i]) + { + case GCA_PORT_INITIALIZING: + case GCA_PORT_EMPTY: + if(instance->pads[pad] != NULL) + { + /* TODO: free pad */ + instance->pads[pad] = NULL; + } + break; + case GCA_PORT_CONNECTED: + if(instance->pads[pad] == NULL) + { + instance->pads[pad] = register_pad(instance); + } + } + } +} + +static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance) { + int slot; + joypad_connection_t *result; + + slot = pad_connection_find_vacant_pad(instance->driver->pad_connection_list); + if(slot < 0) + return NULL; + + result = &(instance->driver->pad_connection_list[slot]); + result->iface = &wiiu_gca_pad_connection; + result->data = result->iface->init(instance, slot, instance->driver->hid_driver); + result->connected = true; + input_pad_connect(slot, instance->driver->pad_driver); + + return result; +} + static bool wiiu_gca_detect(uint16_t vendor_id, uint16_t product_id) { return vendor_id == VID_NINTENDO && product_id == PID_NINTENDO_GCA; } hid_device_t wiiu_gca_hid_device = { + wiiu_gca_init, + wiiu_gca_free, + wiiu_gca_handle_packet, wiiu_gca_detect, "Wii U Gamecube Adapter" }; + +pad_connection_interface_t wiiu_gca_pad_connection = { +/* + wiiu_gca_pad_init, + wiiu_gca_pad_deinit, + wiiu_gca_packet_handler, + wiiu_gca_set_rumble, + wiiu_gca_get_buttons, + wiiu_gca_get_axis, + wiiu_gca_get_name +*/ +}; diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c index 0f049ca728..a3b861bf4d 100644 --- a/input/common/hid/hid_device_driver.c +++ b/input/common/hid/hid_device_driver.c @@ -16,6 +16,8 @@ #include "hid_device_driver.h" +hid_driver_instance_t hid_instance = {0}; + hid_device_t *hid_device_list[] = { &wiiu_gca_hid_device, &ds3_hid_device, @@ -34,3 +36,102 @@ hid_device_t *hid_device_driver_lookup(uint16_t vendor_id, uint16_t product_id) return NULL; } +void hid_pad_connect(hid_driver_instance_t *instance, int pad) +{ + if(!instance || !instance->pad_driver) + return; + + input_pad_connect(pad, instance->pad_driver); +} + +/** + * Fill in instance with data from initialized hid subsystem. + * + * @argument instance the hid_driver_instance_t struct to fill in + * @argument hid_driver the HID driver to initialize + * @argument pad_driver the gamepad driver to handle HID pads detected by the HID driver. + * + * @returns true if init is successful, false otherwise. + */ +bool hid_init(hid_driver_instance_t *instance, + hid_driver_t *hid_driver, + input_device_driver_t *pad_driver, + unsigned slots) +{ + if(!instance || !hid_driver || !pad_driver || slots > MAX_USERS) + return false; + + instance->hid_data = hid_driver->init(instance); + if(!instance->hid_data) + return false; + + instance->pad_connection_list = pad_connection_init(slots); + if(!instance->pad_connection_list) + { + hid_driver->free(instance->hid_data); + instance->hid_data = NULL; + return false; + } + + instance->max_slot = slots; + instance->hid_driver = hid_driver; + instance->pad_driver = pad_driver; + + return true; +} + +/** + * Tear down the HID system set up by hid_init() + * + * @argument instance the hid_driver_instance_t to tear down. + */ +void hid_deinit(hid_driver_instance_t *instance) +{ + if(!instance) + return; + + pad_connection_destroy(instance->pad_connection_list); + + if(instance->hid_driver && instance->hid_data) + { + instance->hid_driver->free(instance->hid_data); + } + + memset(instance, 0, sizeof(hid_driver_instance_t)); +} + +static void hid_device_log_buffer(uint8_t *data, uint32_t len) +{ +#if 0 + int i, offset; + int padding = len % 0x0F; + uint8_t buf[16]; + + RARCH_LOG("%d bytes read:\n", len); + + for(i = 0, offset = 0; i < len; i++) + { + buf[offset] = data[i]; + offset++; + if(offset == 16) + { + offset = 0; + RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + } + + if(padding) + { + for(i = padding; i < 16; i++) + buf[i] = 0xff; + + RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + + RARCH_LOG("=================================\n"); + #endif +} diff --git a/input/common/hid/hid_device_driver.h b/input/common/hid/hid_device_driver.h index a3259aee52..8bb371f70e 100644 --- a/input/common/hid/hid_device_driver.h +++ b/input/common/hid/hid_device_driver.h @@ -19,8 +19,12 @@ #include "../../input_driver.h" #include "../../connect/joypad_connection.h" +#include "../../include/hid_driver.h" typedef struct hid_device { + void *(*init)(hid_driver_instance_t *driver); + void (*free)(void *data); + void (*handle_packet)(void *data, uint8_t *buffer, size_t size); bool (*detect)(uint16_t vid, uint16_t pid); const char *name; } hid_device_t; @@ -28,7 +32,12 @@ typedef struct hid_device { extern hid_device_t wiiu_gca_hid_device; extern hid_device_t ds3_hid_device; extern hid_device_t ds4_hid_device; +extern hid_driver_instance_t hid_instance; hid_device_t *hid_device_driver_lookup(uint16_t vendor_id, uint16_t product_id); +void hid_pad_connect(hid_driver_instance_t *instance, int pad); +bool hid_init(hid_driver_instance_t *instance, hid_driver_t *hid_driver, input_device_driver_t *pad_driver, unsigned slots); +void hid_deinit(hid_driver_instance_t *instance); + #endif /* HID_DEVICE_DRIVER__H */ diff --git a/input/connect/connect_ps3.c b/input/connect/connect_ps3.c index 61269a9e88..f77235cf80 100644 --- a/input/connect/connect_ps3.c +++ b/input/connect/connect_ps3.c @@ -21,10 +21,7 @@ #include #include "joypad_connection.h" #include "../input_defines.h" - -#ifdef WIIU -#include -#endif +#include "../common/hid/hid_device_driver.h" struct hidpad_ps3_data { diff --git a/input/connect/connect_ps4.c b/input/connect/connect_ps4.c index 0356a91537..f011bd3ab8 100644 --- a/input/connect/connect_ps4.c +++ b/input/connect/connect_ps4.c @@ -25,6 +25,7 @@ #include "joypad_connection.h" #include "../input_defines.h" #include "../../driver.h" +#include "../common/hid/hid_device_driver.h" enum connect_ps4_dpad_states { diff --git a/input/connect/connect_wii.c b/input/connect/connect_wii.c index f02e1735be..74865862aa 100644 --- a/input/connect/connect_wii.c +++ b/input/connect/connect_wii.c @@ -26,6 +26,7 @@ #include "joypad_connection.h" #include "../input_defines.h" +#include "../common/hid/hid_device_driver.h" /* wiimote state flags*/ #define WIIMOTE_STATE_DEV_FOUND 0x0001 diff --git a/input/connect/connect_wiiugca.c b/input/connect/connect_wiiugca.c index 7e49969fd2..3803488797 100644 --- a/input/connect/connect_wiiugca.c +++ b/input/connect/connect_wiiugca.c @@ -21,6 +21,7 @@ #include #include "joypad_connection.h" #include "../input_defines.h" +#include "../common/hid/hid_device_driver.h" struct hidpad_wiiugca_data { diff --git a/input/connect/connect_wiiupro.c b/input/connect/connect_wiiupro.c index 836f17856e..d2ad16a968 100644 --- a/input/connect/connect_wiiupro.c +++ b/input/connect/connect_wiiupro.c @@ -23,6 +23,7 @@ #include "joypad_connection.h" #include "../input_defines.h" #include "../../driver.h" +#include "../common/hid/hid_device_driver.h" struct wiiupro_buttons { diff --git a/input/drivers_hid/btstack_hid.c b/input/drivers_hid/btstack_hid.c index 1d277484aa..68f20ea666 100644 --- a/input/drivers_hid/btstack_hid.c +++ b/input/drivers_hid/btstack_hid.c @@ -1439,14 +1439,17 @@ static void btstack_hid_free(const void *data) free(hid); } -static void *btstack_hid_init(void) +static void *btstack_hid_init(joypad_connection_t *connections) { btstack_hid_t *hid = (btstack_hid_t*)calloc(1, sizeof(btstack_hid_t)); if (!hid) goto error; - hid->slots = pad_connection_init(MAX_USERS); + if(connections == NULL) + connections = pad_connection_init(MAX_USERS); + + hid->slots = connections; if (!hid->slots) goto error; diff --git a/input/drivers_hid/iohidmanager_hid.c b/input/drivers_hid/iohidmanager_hid.c index 9ad99ebe2f..ad60d4c969 100644 --- a/input/drivers_hid/iohidmanager_hid.c +++ b/input/drivers_hid/iohidmanager_hid.c @@ -854,14 +854,18 @@ static int iohidmanager_hid_manager_set_device_matching( return 0; } -static void *iohidmanager_hid_init(void) +static void *iohidmanager_hid_init(joypad_connection_t *connections) { iohidmanager_hid_t *hid_apple = (iohidmanager_hid_t*) calloc(1, sizeof(*hid_apple)); if (!hid_apple) goto error; - hid_apple->slots = pad_connection_init(MAX_USERS); + + if (connections == NULL) + connections = pad_connection_init(MAX_USERS); + + hid_apple->slots = connections; if (!hid_apple->slots) goto error; diff --git a/input/drivers_hid/libusb_hid.c b/input/drivers_hid/libusb_hid.c index 44580359fa..f06adb5366 100644 --- a/input/drivers_hid/libusb_hid.c +++ b/input/drivers_hid/libusb_hid.c @@ -546,7 +546,7 @@ static void poll_thread(void *data) } } -static void *libusb_hid_init(void) +static void *libusb_hid_init(joypad_connection_t *connections) { unsigned i, count; int ret; @@ -578,7 +578,10 @@ static void *libusb_hid_init(void) hid->can_hotplug = 0; #endif - hid->slots = pad_connection_init(MAX_USERS); + if (connections == NULL) + connections = pad_connection_init(MAX_USERS); + + hid->slots = connections; if (!hid->slots) goto error; diff --git a/input/drivers_hid/null_hid.c b/input/drivers_hid/null_hid.c index 70a34d4bf8..e7cf00b580 100644 --- a/input/drivers_hid/null_hid.c +++ b/input/drivers_hid/null_hid.c @@ -18,6 +18,7 @@ #include "../input_defines.h" #include "../input_driver.h" +#include "../include/hid_driver.h" typedef struct null_hid { @@ -75,8 +76,10 @@ static int16_t null_hid_joypad_axis(void *data, unsigned port, uint32_t joyaxis) return 0; } -static void *null_hid_init(void) +static void *null_hid_init(hid_driver_instance_t *instance) { + (void)instance; + return (null_hid_t*)calloc(1, sizeof(null_hid_t)); } diff --git a/input/drivers_hid/wiiusb_hid.c b/input/drivers_hid/wiiusb_hid.c index 397f0a0a64..6e3a768bca 100644 --- a/input/drivers_hid/wiiusb_hid.c +++ b/input/drivers_hid/wiiusb_hid.c @@ -574,15 +574,15 @@ static void wiiusb_hid_free(const void *data) free(hid); } -static void *wiiusb_hid_init(void) +static void *wiiusb_hid_init(joypad_connection_t *connections) { - joypad_connection_t *connections = NULL; wiiusb_hid_t *hid = (wiiusb_hid_t*)calloc(1, sizeof(*hid)); if (!hid) goto error; - connections = pad_connection_init(MAX_USERS); + if(connections == NULL) + connections = pad_connection_init(MAX_USERS); if (!connections) goto error; diff --git a/input/include/hid_driver.h b/input/include/hid_driver.h new file mode 100644 index 0000000000..37ae190a51 --- /dev/null +++ b/input/include/hid_driver.h @@ -0,0 +1,63 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2017 - Andrés Suárez + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef HID_DRIVER_H__ +#define HID_DRIVER_H__ + +#include "../connect/joypad_connection.h" +#include "../input_driver.h" + +/* what is 1? */ +#define HID_REPORT_OUTPUT 2 +#define HID_REPORT_FEATURE 3 +/* are there more? */ + +/* + * This is the interface for the HID subsystem. + * + * The handle parameter is the pointer returned by init() and stores the implementation + * state data for the HID driver. + */ + +struct hid_driver +{ + void *(*init)(hid_driver_instance_t *); + bool (*query_pad)(void *handle, unsigned pad); + void (*free)(const void *handle); + bool (*button)(void *handle, unsigned pad, uint16_t button); + void (*get_buttons)(void *handle, unsigned pad, retro_bits_t *state); + int16_t (*axis)(void *handle, unsigned pad, uint32_t axis); + void (*poll)(void *handle); + bool (*set_rumble)(void *handle, unsigned pad, enum retro_rumble_effect effect, uint16_t); + const char *(*name)(void *handle, unsigned pad); + const char *ident; + void (*send_control)(void *handle, uint8_t *buf, size_t size); + int32_t (*set_report)(void *handle, uint8_t, uint8_t, void *data, uint32_t size); + int32_t (*set_idle)(void *handle, uint8_t amount); + int32_t (*set_protocol)(void *handle, uint8_t protocol); + int32_t (*read)(void *handle, void *buf, size_t size); +}; + +struct hid_driver_instance { + hid_driver_t *hid_driver; + void *hid_data; + input_device_driver_t *pad_driver; + joypad_connection_t *pad_connection_list; + unsigned max_slot; +}; + +#endif /* HID_DRIVER_H__ */ diff --git a/input/include/hid_types.h b/input/include/hid_types.h new file mode 100644 index 0000000000..28734e9dd8 --- /dev/null +++ b/input/include/hid_types.h @@ -0,0 +1,24 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2017 - Andrés Suárez + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef HID_TYPES_H__ +#define HID_TYPES_H__ + +typedef struct hid_driver hid_driver_t; +typedef struct hid_driver_instance hid_driver_instance_t; + +#endif /* HID_TYPES_H__ */ diff --git a/input/input_driver.c b/input/input_driver.c index 14ed1c17a9..882d33ca1b 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1782,6 +1782,19 @@ bool input_mouse_button_raw(unsigned port, unsigned id) return false; } +void input_pad_connect(unsigned port, input_device_driver_t *driver) +{ + if(port >= MAX_USERS || !driver) + { + RARCH_ERR("[input]: input_pad_connect: bad parameters\n"); + return; + } + + if(!input_autoconfigure_connect(driver->name(port), NULL, driver->ident, + port, 0, 0)) + input_config_set_device_name(port, driver->name(port)); +} + /** * input_conv_analog_id_to_bind_id: * @idx : Analog key index. diff --git a/input/input_driver.h b/input/input_driver.h index 2ef47c7d24..b366413893 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -35,13 +35,12 @@ #include "input_defines.h" #include "../msg_hash.h" +#include "include/hid_types.h" RETRO_BEGIN_DECLS typedef struct rarch_joypad_driver input_device_driver_t; -typedef struct hid_driver hid_driver_t; - /* Keyboard line reader. Handles textual input in a direct fashion. */ typedef struct input_keyboard_line input_keyboard_line_t; @@ -181,25 +180,6 @@ struct rarch_joypad_driver const char *ident; }; -struct hid_driver -{ - void *(*init)(void); - bool (*query_pad)(void *, unsigned); - void (*free)(const void *); - bool (*button)(void *, unsigned, uint16_t); - void (*get_buttons)(void *, unsigned, retro_bits_t *); - int16_t (*axis)(void *, unsigned, uint32_t); - void (*poll)(void *); - bool (*set_rumble)(void *, unsigned, enum retro_rumble_effect, uint16_t); - const char *(*name)(void *, unsigned); - const char *ident; - void (*send_control)(void *data, uint8_t *buf, size_t size); - int32_t (*set_report)(void *, uint8_t, uint8_t, void *, uint32_t); - int32_t (*set_idle)(void *, uint8_t); - int32_t (*set_protocol)(void *, uint8_t); - -}; - /** * input_driver_find_handle: * @index : index of driver to get handle to. @@ -591,6 +571,15 @@ bool input_joypad_button_raw(const input_device_driver_t *driver, bool input_joypad_hat_raw(const input_device_driver_t *driver, unsigned joypad, unsigned hat_dir, unsigned hat); +/** + * input_pad_connect: + * @port : Joystick number. + * @driver : handle for joypad driver handling joystick's input + * + * Registers a newly connected pad with RetroArch. + **/ +void input_pad_connect(unsigned port, input_device_driver_t *driver); + /** * input_mouse_button_raw: * @port : Mouse number. @@ -848,7 +837,6 @@ extern hid_driver_t iohidmanager_hid; extern hid_driver_t btstack_hid; extern hid_driver_t libusb_hid; extern hid_driver_t wiiusb_hid; -extern hid_driver_t wiiu_hid; extern hid_driver_t null_hid; #endif diff --git a/wiiu/include/wiiu/pad_driver.h b/wiiu/include/wiiu/pad_driver.h index 8a93798de7..067bc34d67 100644 --- a/wiiu/include/wiiu/pad_driver.h +++ b/wiiu/include/wiiu/pad_driver.h @@ -32,8 +32,8 @@ #include "../../input/input_driver.h" #include "../../input/common/hid/hid_device_driver.h" -#include "../../input/connect/joypad_connection.h" #include "../../tasks/tasks_internal.h" +#include "../../input/connect/joypad_connection.h" #include "../../retroarch.h" #include "../../verbosity.h" #include "../../command.h" @@ -119,8 +119,8 @@ struct _wiiu_pad_functions { typedef struct wiiu_hid { /* used to register for HID notifications */ HIDClient *client; - /* list of HID pads */ - joypad_connection_t *connections; + /* pointer to HID driver state */ + hid_driver_instance_t *driver; /* size of connections list */ unsigned connections_size; /* thread state data for HID polling thread */ @@ -135,13 +135,14 @@ typedef struct wiiu_adapter wiiu_adapter_t; struct wiiu_adapter { wiiu_adapter_t *next; + hid_device_t *driver; + void *driver_handle; wiiu_hid_t *hid; uint8_t state; uint8_t *rx_buffer; int32_t rx_size; uint8_t *tx_buffer; int32_t tx_size; - int32_t slot; uint32_t handle; uint8_t interface_index; }; diff --git a/wiiu/include/wiiu/syshid.h b/wiiu/include/wiiu/syshid.h index 1f55718dd9..664e52eef6 100644 --- a/wiiu/include/wiiu/syshid.h +++ b/wiiu/include/wiiu/syshid.h @@ -1,15 +1,6 @@ #pragma once #include -/* - * Report types for the report_type parameter in HIDSetReport() - */ - -/* what is 1? */ -#define HID_REPORT_OUTPUT 2 -#define HID_REPORT_FEATURE 3 -/* are there more? */ - typedef struct { uint32_t handle; diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c index 5cae6d710f..272bd2e7c0 100644 --- a/wiiu/input/hidpad_driver.c +++ b/wiiu/input/hidpad_driver.c @@ -15,6 +15,8 @@ */ #include +#include "../../input/include/hid_driver.h" +#include "../../input/common/hid/hid_device_driver.h" static bool hidpad_init(void *data); static bool hidpad_query_pad(unsigned pad); @@ -27,51 +29,25 @@ static const char *hidpad_name(unsigned pad); static bool ready = false; -static wiiu_hid_t *hid_data; -static hid_driver_t *hid_driver; - static unsigned to_slot(unsigned pad) { return pad - (WIIU_WIIMOTE_CHANNELS+1); } -const void *get_hid_data(void) +static bool init_hid_driver(void) { - return hid_data; -} - -static hid_driver_t *init_hid_driver(void) -{ - joypad_connection_t *connections = NULL; unsigned connections_size = MAX_USERS - (WIIU_WIIMOTE_CHANNELS+1); - hid_data = (wiiu_hid_t *)wiiu_hid.init(); - connections = pad_connection_init(connections_size); + memset(&hid_instance, 0, sizeof(hid_instance)); - if (!hid_data || !connections) - goto error; - - hid_data->connections = connections; - hid_data->connections_size = connections_size; - return &wiiu_hid; - -error: - if (connections) - free(connections); - if (hid_data) - { - wiiu_hid.free(hid_data); - hid_data = NULL; - } - return NULL; + return hid_init(&hid_instance, &wiiu_hid, &hidpad_driver, connections_size); } static bool hidpad_init(void *data) { (void *)data; - hid_driver = init_hid_driver(); - if (!hid_driver) + if(!init_hid_driver()) { RARCH_ERR("Failed to initialize HID driver.\n"); return false; @@ -92,16 +68,7 @@ static void hidpad_destroy(void) { ready = false; - if(hid_driver) { - hid_driver->free(hid_data); - hid_data = NULL; - hid_driver = NULL; - } - - if(hid_data) { - free(hid_data); - hid_data = NULL; - } + hid_deinit(&hid_instance); } static bool hidpad_button(unsigned pad, uint16_t button) diff --git a/wiiu/input/kpad_driver.c b/wiiu/input/kpad_driver.c index 48a7f428c3..51d203cec0 100644 --- a/wiiu/input/kpad_driver.c +++ b/wiiu/input/kpad_driver.c @@ -117,7 +117,7 @@ static void kpad_register(unsigned channel, uint8_t device_type) if (wiimotes[channel].type != device_type) { wiimotes[channel].type = device_type; - pad_functions.connect(to_retro_pad(channel), &kpad_driver); + input_pad_connect(to_retro_pad(channel), &kpad_driver); } } diff --git a/wiiu/input/pad_functions.c b/wiiu/input/pad_functions.c index fe352e5da4..132bffa146 100644 --- a/wiiu/input/pad_functions.c +++ b/wiiu/input/pad_functions.c @@ -88,16 +88,8 @@ void wiiu_pad_read_axis_data(uint32_t axis, axis_data *data) } } -void wiiu_pad_connect(unsigned pad, input_device_driver_t *driver) -{ - if(!input_autoconfigure_connect(driver->name(pad), NULL, driver->ident, - pad, VID_NONE, PID_NONE)) - input_config_set_device_name(pad, driver->name(pad)); -} - wiiu_pad_functions_t pad_functions = { wiiu_pad_get_axis_value, wiiu_pad_set_axis_value, wiiu_pad_read_axis_data, - wiiu_pad_connect }; diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index f2656a4cbb..cb09db3346 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -19,13 +19,15 @@ static wiiu_event_list events; static wiiu_adapter_list adapters; +static void log_buffer(uint8_t *data, uint32_t len); + static bool wiiu_hid_joypad_query(void *data, unsigned slot) { wiiu_hid_t *hid = (wiiu_hid_t *)data; - if (!hid) + if (!hid || !hid->driver) return false; - return slot < hid->connections_size; + return slot < hid->driver->max_slot; } static const char *wiiu_hid_joypad_name(void *data, unsigned slot) @@ -35,7 +37,7 @@ static const char *wiiu_hid_joypad_name(void *data, unsigned slot) wiiu_hid_t *hid = (wiiu_hid_t *)data; - return hid->connections[slot].iface->get_name(data); + return hid->driver->pad_connection_list[slot].iface->get_name(data); } static void wiiu_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state) @@ -75,7 +77,7 @@ static int16_t wiiu_hid_joypad_axis(void *data, unsigned port, uint32_t joyaxis) return 0; } -static void *wiiu_hid_init(void) +static void *wiiu_hid_init(hid_driver_instance_t *driver) { RARCH_LOG("[hid]: initializing HID subsystem\n"); wiiu_hid_t *hid = new_hid(); @@ -84,6 +86,8 @@ static void *wiiu_hid_init(void) if (!hid || !client) goto error; + hid->driver = driver; + wiiu_hid_init_lists(); start_polling_thread(hid); if (!hid->polling_thread) @@ -98,6 +102,7 @@ static void *wiiu_hid_init(void) error: RARCH_LOG("[hid]: initialization failed. cleaning up.\n"); + stop_polling_thread(hid); delete_hid(hid); delete_hidclient(client); @@ -197,6 +202,26 @@ static int32_t wiiu_hid_set_protocol(void *data, uint8_t protocol) NULL, NULL); } +static int32_t wiiu_hid_read(void *data, void *buffer, size_t size) +{ + wiiu_adapter_t *adapter = (wiiu_adapter_t *)data; + + if(!adapter) + return -1; + + if(size > adapter->rx_size) + return -1; + +#if 1 + int32_t result = HIDRead(adapter->handle, buffer, size, NULL, NULL); + log_buffer(buffer, size); + return result; +#else + return HIDRead(adapter->handle, buffer, size, NULL, NULL); +#endif +} + + static void start_polling_thread(wiiu_hid_t *hid) { OSThreadAttributes attributes = OS_THREAD_ATTRIB_AFFINITY_CPU2; @@ -357,23 +382,12 @@ static void wiiu_hid_attach(wiiu_hid_t *hid, wiiu_attach_event *event) } adapter->hid = hid; - adapter->slot = pad_connection_pad_init(hid->connections, - "hid", event->vendor_id, event->product_id, adapter, - &wiiu_hid); + adapter->driver = event->driver; - if(adapter->slot < 0) - { - RARCH_ERR("[hid]: No available slots.\n"); - goto error; - } - - RARCH_LOG("[hid]: got slot %d\n", adapter->slot); - - if(!pad_connection_has_interface(hid->connections, adapter->slot)) - { - RARCH_ERR("[hid]: Interface not found for HID device with vid=0x%04x pid=0x%04x\n", - event->vendor_id, event->product_id); - goto error; + adapter->driver_handle = adapter->driver->init(hid->driver); + if(adapter->driver_handle == NULL) { + RARCH_ERR("[hid]: Failed to initialize driver: %s\n", + adapter->driver->name); } RARCH_LOG("[hid]: adding to adapter list\n"); @@ -446,16 +460,6 @@ static void log_buffer(uint8_t *data, uint32_t len) } -static void wiiu_hid_do_read(wiiu_adapter_t *adapter, - uint8_t *data, uint32_t length) -{ -#if 0 - log_buffer(data, length); -#endif - - /* TODO: get this data to the connect_xxx driver somehow. */ -} - static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, uint8_t *buffer, uint32_t buffer_size, void *userdata) { @@ -468,12 +472,13 @@ static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, if(adapter->hid->polling_thread_quit) { - RARCH_LOG("Shutting down read loop for slot %d\n", adapter->slot); + RARCH_LOG("Shutting down read loop for device: %s\n", + adapter->driver->name); adapter->state = ADAPTER_STATE_DONE; return; } - wiiu_hid_do_read(adapter, buffer, buffer_size); + adapter->driver->handle_packet(adapter->driver_handle, buffer, buffer_size); adapter->state = ADAPTER_STATE_READING; HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, @@ -644,6 +649,11 @@ static void delete_adapter(wiiu_adapter_t *adapter) free(adapter->tx_buffer); adapter->tx_buffer = NULL; } + if(adapter->driver && adapter->driver_handle) { + adapter->driver->free(adapter->driver_handle); + adapter->driver_handle = NULL; + adapter->driver = NULL; + } free(adapter); } @@ -707,6 +717,7 @@ hid_driver_t wiiu_hid = { wiiu_hid_send_control, wiiu_hid_set_report, wiiu_hid_set_idle, - wiiu_hid_set_protocol + wiiu_hid_set_protocol, + wiiu_hid_read, }; diff --git a/wiiu/input/wiiu_hid.h b/wiiu/input/wiiu_hid.h index 1c2ede6f73..b1ce59a6c3 100644 --- a/wiiu/input/wiiu_hid.h +++ b/wiiu/input/wiiu_hid.h @@ -18,6 +18,7 @@ #define __WIIU_HID__H #include +#include "../../input/include/hid_driver.h" #define DEVICE_UNUSED 0 #define DEVICE_USED 1 diff --git a/wiiu/input/wpad_driver.c b/wiiu/input/wpad_driver.c index b707c63cb5..786f883cda 100644 --- a/wiiu/input/wpad_driver.c +++ b/wiiu/input/wpad_driver.c @@ -184,7 +184,7 @@ static void wpad_poll(void) static bool wpad_init(void *data) { - pad_functions.connect(PAD_GAMEPAD, &wpad_driver); + input_pad_connect(PAD_GAMEPAD, &wpad_driver); wpad_poll(); ready = true; From 4b9d5c0ab7f7ca07ea090e52a0500c5071dd1704 Mon Sep 17 00:00:00 2001 From: gblues Date: Sun, 11 Mar 2018 22:08:06 -0700 Subject: [PATCH 078/517] Start implementing "detach" code path == DETAILS We're at a point where we need to do more than just clean up a local data structure, so I've started implementing the "detach" part of the code so that everything gets cleaned up properly. Also, added error handling inside the polling thread. == TESTING Have not tested yet. --- input/common/hid/device_wiiu_gca.c | 108 +++++++++++++++++------------ wiiu/input/wiiu_hid.c | 37 ++++++++++ wiiu/input/wiiu_hid.h | 1 + 3 files changed, 103 insertions(+), 43 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 211f67dfbb..7c8fa9296a 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -28,12 +28,14 @@ static uint8_t activation_packet[] = { 0x13 }; typedef struct wiiu_gca_instance { hid_driver_instance_t *driver; + bool online; uint8_t device_state[37]; joypad_connection_t *pads[4]; } wiiu_gca_instance_t; static void update_pad_state(wiiu_gca_instance_t *instance); static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance); +static void unregister_pad(wiiu_gca_instance_t *instance, int slot); extern pad_connection_interface_t wiiu_gca_pad_connection; @@ -45,21 +47,29 @@ static void *wiiu_gca_init(hid_driver_instance_t *driver) driver->hid_driver->send_control(driver->hid_data, activation_packet, sizeof(activation_packet)); driver->hid_driver->read(driver->hid_data, instance->device_state, sizeof(instance->device_state)); + instance->online = true; return instance; } static void wiiu_gca_free(void *data) { - wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; - if(instance) { - free(instance); + wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; + int i; + + if(instance) { + instance->online = false; + + for(i = 0; i < 4; i++) + unregister_pad(instance, i); + + free(instance); } } static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size) { wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; - if(!instance) + if(!instance || !instance->online) return; if(size > sizeof(instance->device_state)) @@ -71,58 +81,70 @@ static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size) static void update_pad_state(wiiu_gca_instance_t *instance) { - int i, pad; + int i, pad; + if(!instance || !instance->online) + return; - /* process each pad */ - for(i = 1; i < 37; i += 9) - { - pad = i / 9; - switch(instance->device_state[i]) - { - case GCA_PORT_INITIALIZING: - case GCA_PORT_EMPTY: - if(instance->pads[pad] != NULL) - { - /* TODO: free pad */ - instance->pads[pad] = NULL; - } - break; - case GCA_PORT_CONNECTED: - if(instance->pads[pad] == NULL) - { - instance->pads[pad] = register_pad(instance); - } - } - } + /* process each pad */ + for(i = 1; i < 37; i += 9) + { + pad = i / 9; + switch(instance->device_state[i]) + { + case GCA_PORT_INITIALIZING: + case GCA_PORT_EMPTY: + if(instance->pads[pad] != NULL) + unregister_pad(instance, pad); + break; + case GCA_PORT_CONNECTED: + if(instance->pads[pad] == NULL) + instance->pads[pad] = register_pad(instance); + } + } } static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance) { - int slot; - joypad_connection_t *result; + int slot; + joypad_connection_t *result; - slot = pad_connection_find_vacant_pad(instance->driver->pad_connection_list); - if(slot < 0) - return NULL; + if(!instance || !instance->online) + return NULL; - result = &(instance->driver->pad_connection_list[slot]); - result->iface = &wiiu_gca_pad_connection; - result->data = result->iface->init(instance, slot, instance->driver->hid_driver); - result->connected = true; - input_pad_connect(slot, instance->driver->pad_driver); + slot = pad_connection_find_vacant_pad(instance->driver->pad_connection_list); + if(slot < 0) + return NULL; - return result; + result = &(instance->driver->pad_connection_list[slot]); + result->iface = &wiiu_gca_pad_connection; + result->data = result->iface->init(instance, slot, instance->driver->hid_driver); + result->connected = true; + input_pad_connect(slot, instance->driver->pad_driver); + + return result; +} + +static void unregister_pad(wiiu_gca_instance_t *instance, int slot) +{ + if(!instance || slot < 0 || slot >= 4 || instance->pads[slot] == NULL) + return; + + joypad_connection_t *pad = instance->pads[slot]; + instance->pads[slot] = NULL; + pad->iface->deinit(pad->data); + pad->data = NULL; + pad->connected = false; } static bool wiiu_gca_detect(uint16_t vendor_id, uint16_t product_id) { - return vendor_id == VID_NINTENDO && product_id == PID_NINTENDO_GCA; + return vendor_id == VID_NINTENDO && product_id == PID_NINTENDO_GCA; } hid_device_t wiiu_gca_hid_device = { - wiiu_gca_init, - wiiu_gca_free, - wiiu_gca_handle_packet, - wiiu_gca_detect, - "Wii U Gamecube Adapter" + wiiu_gca_init, + wiiu_gca_free, + wiiu_gca_handle_packet, + wiiu_gca_detect, + "Wii U Gamecube Adapter" }; pad_connection_interface_t wiiu_gca_pad_connection = { diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index cb09db3346..7a2cf113a4 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -329,6 +329,29 @@ static wiiu_attach_event *synchronized_get_events_list(void) return list; } +static wiiu_adapter_t *synchronized_remove_from_adapters_list(uint32_t handle) +{ + OSFastMutex_Lock(&(adapters.lock)); + wiiu_adapter_t *iterator, *prev = NULL; + + for(iterator = adapters.list; iterator != NULL; iterator = iterator->next) + { + if(iterator->handle == handle) + { + /* we're at the start of the list, so just re-assign head */ + if(prev == NULL) + adapters.list = iterator->next; + else + prev->next = iterator->next; + break; + } + prev = iterator; + } + OSFastMutex_Unlock(&(adapters.lock)); + + return iterator; +} + static void synchronized_add_to_adapters_list(wiiu_adapter_t *adapter) { OSFastMutex_Lock(&(adapters.lock)); @@ -368,6 +391,13 @@ error: static void wiiu_hid_detach(wiiu_hid_t *hid, wiiu_attach_event *event) { + wiiu_adapter_t *adapter = synchronized_remove_from_adapters_list(event->handle); + + if(adapter) { + adapter->driver->free(adapter->driver_handle); + adapter->driver_handle = NULL; + delete_adapter(adapter); + } } @@ -478,6 +508,13 @@ static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, return; } + if(error) + { + RARCH_ERR("Read failed with error 0x%08x\n", error); + adapter->state = ADAPTER_STATE_DONE; + return; + } + adapter->driver->handle_packet(adapter->driver_handle, buffer, buffer_size); adapter->state = ADAPTER_STATE_READING; diff --git a/wiiu/input/wiiu_hid.h b/wiiu/input/wiiu_hid.h index b1ce59a6c3..717980e7c8 100644 --- a/wiiu/input/wiiu_hid.h +++ b/wiiu/input/wiiu_hid.h @@ -48,6 +48,7 @@ static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list); static void wiiu_hid_attach(wiiu_hid_t *hid, wiiu_attach_event *event); static void wiiu_hid_detach(wiiu_hid_t *hid, wiiu_attach_event *event); static void synchronized_add_to_adapters_list(wiiu_adapter_t *adapter); +static wiiu_adapter_t *synchronized_remove_from_adapters_list(uint32_t handle); static void synchronized_add_event(wiiu_attach_event *event); static void wiiu_start_read_loop(wiiu_adapter_t *adapter); static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, From dc6f4c23ed26f5c010fcae9a15b5ba7f0dd521b5 Mon Sep 17 00:00:00 2001 From: gblues Date: Sun, 11 Mar 2018 23:18:48 -0700 Subject: [PATCH 079/517] Rename hid_driver_instance members for clarity --- input/common/hid/device_wiiu_gca.c | 10 +++++----- input/common/hid/hid_device_driver.c | 20 ++++++++++---------- input/include/hid_driver.h | 6 +++--- wiiu/input/wiiu_hid.c | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 7c8fa9296a..ee721c3c7e 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -45,8 +45,8 @@ static void *wiiu_gca_init(hid_driver_instance_t *driver) memset(instance, 0, sizeof(wiiu_gca_instance_t)); instance->driver = driver; - driver->hid_driver->send_control(driver->hid_data, activation_packet, sizeof(activation_packet)); - driver->hid_driver->read(driver->hid_data, instance->device_state, sizeof(instance->device_state)); + driver->os_driver->send_control(driver->os_driver_data, activation_packet, sizeof(activation_packet)); + driver->os_driver->read(driver->os_driver_data, instance->device_state, sizeof(instance->device_state)); instance->online = true; return instance; @@ -110,13 +110,13 @@ static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance) { if(!instance || !instance->online) return NULL; - slot = pad_connection_find_vacant_pad(instance->driver->pad_connection_list); + slot = pad_connection_find_vacant_pad(instance->driver->pad_list); if(slot < 0) return NULL; - result = &(instance->driver->pad_connection_list[slot]); + result = &(instance->driver->pad_list[slot]); result->iface = &wiiu_gca_pad_connection; - result->data = result->iface->init(instance, slot, instance->driver->hid_driver); + result->data = result->iface->init(instance, slot, instance->driver->os_driver); result->connected = true; input_pad_connect(slot, instance->driver->pad_driver); diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c index a3b861bf4d..e850188f07 100644 --- a/input/common/hid/hid_device_driver.c +++ b/input/common/hid/hid_device_driver.c @@ -61,20 +61,20 @@ bool hid_init(hid_driver_instance_t *instance, if(!instance || !hid_driver || !pad_driver || slots > MAX_USERS) return false; - instance->hid_data = hid_driver->init(instance); - if(!instance->hid_data) + instance->os_driver_data = hid_driver->init(instance); + if(!instance->os_driver_data) return false; - instance->pad_connection_list = pad_connection_init(slots); - if(!instance->pad_connection_list) + instance->pad_list = pad_connection_init(slots); + if(!instance->pad_list) { - hid_driver->free(instance->hid_data); - instance->hid_data = NULL; + hid_driver->free(instance->os_driver_data); + instance->os_driver_data = NULL; return false; } instance->max_slot = slots; - instance->hid_driver = hid_driver; + instance->os_driver = hid_driver; instance->pad_driver = pad_driver; return true; @@ -90,11 +90,11 @@ void hid_deinit(hid_driver_instance_t *instance) if(!instance) return; - pad_connection_destroy(instance->pad_connection_list); + pad_connection_destroy(instance->pad_list); - if(instance->hid_driver && instance->hid_data) + if(instance->os_driver && instance->os_driver_data) { - instance->hid_driver->free(instance->hid_data); + instance->os_driver->free(instance->os_driver_data); } memset(instance, 0, sizeof(hid_driver_instance_t)); diff --git a/input/include/hid_driver.h b/input/include/hid_driver.h index 37ae190a51..59b5a62d28 100644 --- a/input/include/hid_driver.h +++ b/input/include/hid_driver.h @@ -53,10 +53,10 @@ struct hid_driver }; struct hid_driver_instance { - hid_driver_t *hid_driver; - void *hid_data; + hid_driver_t *os_driver; + void *os_driver_data; input_device_driver_t *pad_driver; - joypad_connection_t *pad_connection_list; + joypad_connection_t *pad_list; unsigned max_slot; }; diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index 7a2cf113a4..8c82209704 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -37,7 +37,7 @@ static const char *wiiu_hid_joypad_name(void *data, unsigned slot) wiiu_hid_t *hid = (wiiu_hid_t *)data; - return hid->driver->pad_connection_list[slot].iface->get_name(data); + return hid->driver->pad_list[slot].iface->get_name(data); } static void wiiu_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state) From 180d6a28bfa3097bc24c7649afd2ca4ba7a11cfa Mon Sep 17 00:00:00 2001 From: gblues Date: Sat, 24 Mar 2018 22:35:22 -0700 Subject: [PATCH 080/517] Fix up HID device driver initialization == DETAILS Turns out the cause of the crash was a bad cast, resulting in a function call to nowhere. Also, I think the DSI exception handler only works on the primary core; when this was happening in the background thread, I got a black screen error instead. Next up: finishing up the GCA driver. --- input/common/hid/device_ds3.c | 2 +- input/common/hid/device_ds4.c | 2 +- input/common/hid/device_wiiu_gca.c | 30 ++++--- input/common/hid/hid_device_driver.h | 3 +- wiiu/input/hidpad_driver.c | 4 +- wiiu/input/wiiu_hid.c | 117 +++++++++++++++++++-------- wiiu/input/wiiu_hid.h | 7 +- 7 files changed, 113 insertions(+), 52 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index 569d4d7f79..8b156d2ef5 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -21,7 +21,7 @@ struct ds3_instance { void *handle; }; -static void *ds3_init(hid_driver_instance_t *hid_driver) +static void *ds3_init(void *handle) { return NULL; } diff --git a/input/common/hid/device_ds4.c b/input/common/hid/device_ds4.c index 6c8b77ac2f..861de2751f 100644 --- a/input/common/hid/device_ds4.c +++ b/input/common/hid/device_ds4.c @@ -21,7 +21,7 @@ struct ds4_instance { void *handle; }; -static void *ds4_init(hid_driver_instance_t *hid_driver) +static void *ds4_init(void *handle) { return NULL; } diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index ee721c3c7e..960f2f3016 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -27,7 +27,7 @@ static uint8_t activation_packet[] = { 0x13 }; #define GCA_PORT_CONNECTED 0x14 typedef struct wiiu_gca_instance { - hid_driver_instance_t *driver; + void *handle; bool online; uint8_t device_state[37]; joypad_connection_t *pads[4]; @@ -39,17 +39,29 @@ static void unregister_pad(wiiu_gca_instance_t *instance, int slot); extern pad_connection_interface_t wiiu_gca_pad_connection; -static void *wiiu_gca_init(hid_driver_instance_t *driver) +static void *wiiu_gca_init(void *handle) { + RARCH_LOG("[gca]: allocating driver instance...\n"); wiiu_gca_instance_t *instance = calloc(1, sizeof(wiiu_gca_instance_t)); + if(instance == NULL) goto error; + RARCH_LOG("[gca]: zeroing memory...\n"); memset(instance, 0, sizeof(wiiu_gca_instance_t)); - instance->driver = driver; + instance->handle = handle; - driver->os_driver->send_control(driver->os_driver_data, activation_packet, sizeof(activation_packet)); - driver->os_driver->read(driver->os_driver_data, instance->device_state, sizeof(instance->device_state)); + RARCH_LOG("[gca]: sending activation packet to device...\n"); + hid_instance.os_driver->send_control(handle, activation_packet, sizeof(activation_packet)); + RARCH_LOG("[gca]: reading initial state packet...\n"); + hid_instance.os_driver->read(handle, instance->device_state, sizeof(instance->device_state)); instance->online = true; + RARCH_LOG("[gca]: init done\n"); return instance; + + error: + RARCH_ERR("[gca]: init failed\n"); + if(instance) + free(instance); + return NULL; } static void wiiu_gca_free(void *data) { @@ -110,15 +122,15 @@ static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance) { if(!instance || !instance->online) return NULL; - slot = pad_connection_find_vacant_pad(instance->driver->pad_list); + slot = pad_connection_find_vacant_pad(hid_instance.pad_list); if(slot < 0) return NULL; - result = &(instance->driver->pad_list[slot]); + result = &(hid_instance.pad_list[slot]); result->iface = &wiiu_gca_pad_connection; - result->data = result->iface->init(instance, slot, instance->driver->os_driver); + result->data = result->iface->init(instance, slot, hid_instance.os_driver); result->connected = true; - input_pad_connect(slot, instance->driver->pad_driver); + input_pad_connect(slot, hid_instance.pad_driver); return result; } diff --git a/input/common/hid/hid_device_driver.h b/input/common/hid/hid_device_driver.h index 8bb371f70e..8cd9f19944 100644 --- a/input/common/hid/hid_device_driver.h +++ b/input/common/hid/hid_device_driver.h @@ -20,9 +20,10 @@ #include "../../input_driver.h" #include "../../connect/joypad_connection.h" #include "../../include/hid_driver.h" +#include "../../../verbosity.h" typedef struct hid_device { - void *(*init)(hid_driver_instance_t *driver); + void *(*init)(void *handle); void (*free)(void *data); void (*handle_packet)(void *data, uint8_t *buffer, size_t size); bool (*detect)(uint16_t vid, uint16_t pid); diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c index 272bd2e7c0..8991d156ba 100644 --- a/wiiu/input/hidpad_driver.c +++ b/wiiu/input/hidpad_driver.c @@ -108,10 +108,8 @@ static int16_t hidpad_axis(unsigned pad, uint32_t axis) static void hidpad_poll(void) { -#if 0 if (ready) - hid_driver->poll(hid_data); -#endif + hid_instance.os_driver->poll(hid_instance.os_driver_data); } static const char *hidpad_name(unsigned pad) diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index 8c82209704..aa8b78d030 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -134,7 +134,11 @@ static void wiiu_hid_free(const void *data) static void wiiu_hid_poll(void *data) { - (void)data; + wiiu_hid_t *hid = (wiiu_hid_t *)data; + if(hid == NULL) + return; + + synchronized_process_adapters(hid); } static void wiiu_hid_send_control(void *data, uint8_t *buf, size_t size) @@ -310,6 +314,46 @@ static void log_device(HIDDevice *device) } +static uint8_t try_init_driver(wiiu_adapter_t *adapter) +{ + adapter->driver_handle = adapter->driver->init(adapter); + if(adapter->driver_handle == NULL) { + RARCH_ERR("[hid]: Failed to initialize driver: %s\n", + adapter->driver->name); + return ADAPTER_STATE_DONE; + } + + return ADAPTER_STATE_READY; +} + +static void synchronized_process_adapters(wiiu_hid_t *hid) +{ + wiiu_adapter_t *adapter = NULL; + + OSFastMutex_Lock(&(adapters.lock)); + for(adapter = adapters.list; adapter != NULL; adapter = adapter->next) + { + switch(adapter->state) + { + case ADAPTER_STATE_DONE: + break; + case ADAPTER_STATE_NEW: + adapter->state = try_init_driver(adapter); + break; + case ADAPTER_STATE_READY: + case ADAPTER_STATE_READING: +#if 0 + adapter->driver->poll(); +#endif + break; + default: + RARCH_ERR("[hid]: Invalid adapter state: %d\n", adapter->state); + break; + } + } + OSFastMutex_Unlock(&(adapters.lock)); +} + static void synchronized_add_event(wiiu_attach_event *event) { OSFastMutex_Lock(&(events.lock)); @@ -413,12 +457,7 @@ static void wiiu_hid_attach(wiiu_hid_t *hid, wiiu_attach_event *event) adapter->hid = hid; adapter->driver = event->driver; - - adapter->driver_handle = adapter->driver->init(hid->driver); - if(adapter->driver_handle == NULL) { - RARCH_ERR("[hid]: Failed to initialize driver: %s\n", - adapter->driver->name); - } + adapter->state = ADAPTER_STATE_NEW; RARCH_LOG("[hid]: adding to adapter list\n"); synchronized_add_to_adapters_list(adapter); @@ -434,13 +473,11 @@ error: void wiiu_start_read_loop(wiiu_adapter_t *adapter) { - adapter->state = ADAPTER_STATE_READING; -#if 0 - RARCH_LOG("HIDRead(0x%08x, 0x%08x, %d, 0x%08x, 0x%08x)\n", - adapter->handle, adapter->rx_buffer, adapter->rx_size, - wiiu_hid_read_loop_callback, adapter); -#endif - HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, wiiu_hid_read_loop_callback, adapter); + HIDRead(adapter->handle, + adapter->rx_buffer, + adapter->rx_size, + wiiu_hid_read_loop_callback, + adapter); } /** @@ -493,32 +530,42 @@ static void log_buffer(uint8_t *data, uint32_t len) static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, uint8_t *buffer, uint32_t buffer_size, void *userdata) { - wiiu_adapter_t *adapter = (wiiu_adapter_t *)userdata; - if(!adapter) - { - RARCH_ERR("read_loop_callback: bad userdata\n"); - return; - } + wiiu_adapter_t *adapter = (wiiu_adapter_t *)userdata; + if(!adapter) + { + RARCH_ERR("read_loop_callback: bad userdata\n"); + return; + } - if(adapter->hid->polling_thread_quit) - { - RARCH_LOG("Shutting down read loop for device: %s\n", + if(adapter->hid->polling_thread_quit) + { + RARCH_LOG("Shutting down read loop for device: %s\n", adapter->driver->name); - adapter->state = ADAPTER_STATE_DONE; - return; - } + adapter->state = ADAPTER_STATE_DONE; + } - if(error) - { - RARCH_ERR("Read failed with error 0x%08x\n", error); - adapter->state = ADAPTER_STATE_DONE; - return; - } + if(adapter->state == ADAPTER_STATE_READY || + adapter->state == ADAPTER_STATE_READING) { - adapter->driver->handle_packet(adapter->driver_handle, buffer, buffer_size); + adapter->state = ADAPTER_STATE_READING; - adapter->state = ADAPTER_STATE_READING; - HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, + if(error) + { + int16_t r1 = (error & 0x0000FFFF); + int16_t r2 = ((error & 0xFFFF0000) >> 16); + RARCH_ERR("[hid]: read failed: %08x (%d:%d)\n", error, r2, r1); + } else { +#if 0 + adapter->driver->handle_packet(adapter->driver_handle, buffer, buffer_size); +#endif + } + } + + /* this can also get set if something goes wrong in initialization */ + if(adapter->state == ADAPTER_STATE_DONE) + return; + + HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, wiiu_hid_read_loop_callback, adapter); } diff --git a/wiiu/input/wiiu_hid.h b/wiiu/input/wiiu_hid.h index 717980e7c8..59ad898fea 100644 --- a/wiiu/input/wiiu_hid.h +++ b/wiiu/input/wiiu_hid.h @@ -24,8 +24,10 @@ #define DEVICE_USED 1 #define ADAPTER_STATE_NEW 0 -#define ADAPTER_STATE_READING 1 -#define ADAPTER_STATE_DONE 2 +#define ADAPTER_STATE_READY 1 +#define ADAPTER_STATE_READING 2 +#define ADAPTER_STATE_DONE 3 + static void *alloc_zeroed(size_t alignment, size_t size); static OSThread *new_thread(void); @@ -47,6 +49,7 @@ static wiiu_attach_event *synchronized_get_events_list(void); static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list); static void wiiu_hid_attach(wiiu_hid_t *hid, wiiu_attach_event *event); static void wiiu_hid_detach(wiiu_hid_t *hid, wiiu_attach_event *event); +static void synchronized_process_adapters(wiiu_hid_t *hid); static void synchronized_add_to_adapters_list(wiiu_adapter_t *adapter); static wiiu_adapter_t *synchronized_remove_from_adapters_list(uint32_t handle); static void synchronized_add_event(wiiu_attach_event *event); From 8a4c5086fb20045b3e7a23b972b15fc9171da703 Mon Sep 17 00:00:00 2001 From: gblues Date: Sun, 25 Mar 2018 22:59:30 -0700 Subject: [PATCH 081/517] Finish HID implementation for WiiU GCA adapter == DETAILS (I think) - Uncomment the call in the read loop to start feeding packets to the driver - implement the GCA packet driver - implement the pad interface - fix indentations in GCA driver == TESTING Compiles. Haven't tested yet. --- input/common/hid/device_wiiu_gca.c | 248 ++++++++++++++++++++++++----- input/drivers/wiiu_input.c | 4 + wiiu/input/wiiu_hid.c | 2 - 3 files changed, 213 insertions(+), 41 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 960f2f3016..79fb2ae59d 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ - +#include #include "hid_device_driver.h" #ifdef WII @@ -33,35 +33,44 @@ typedef struct wiiu_gca_instance { joypad_connection_t *pads[4]; } wiiu_gca_instance_t; +typedef struct gca_pad_data +{ + void *gca_handle; // instance handle for the GCA adapter + hid_driver_t *driver; // HID system driver interface + uint8_t data[9]; // pad data + uint32_t slot; // slot this pad occupies + uint32_t buttons; // digital button state + char *name; // name of the pad +} gca_pad_t; + + static void update_pad_state(wiiu_gca_instance_t *instance); -static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance); -static void unregister_pad(wiiu_gca_instance_t *instance, int slot); +static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port); +static void unregister_pad(wiiu_gca_instance_t *instance, int port); +static void set_pad_name_for_port(gca_pad_t *pad, int port); extern pad_connection_interface_t wiiu_gca_pad_connection; static void *wiiu_gca_init(void *handle) { - RARCH_LOG("[gca]: allocating driver instance...\n"); - wiiu_gca_instance_t *instance = calloc(1, sizeof(wiiu_gca_instance_t)); - if(instance == NULL) goto error; - RARCH_LOG("[gca]: zeroing memory...\n"); - memset(instance, 0, sizeof(wiiu_gca_instance_t)); - instance->handle = handle; + RARCH_LOG("[gca]: allocating driver instance...\n"); + wiiu_gca_instance_t *instance = calloc(1, sizeof(wiiu_gca_instance_t)); + if(instance == NULL) goto error; + memset(instance, 0, sizeof(wiiu_gca_instance_t)); + instance->handle = handle; - RARCH_LOG("[gca]: sending activation packet to device...\n"); - hid_instance.os_driver->send_control(handle, activation_packet, sizeof(activation_packet)); - RARCH_LOG("[gca]: reading initial state packet...\n"); - hid_instance.os_driver->read(handle, instance->device_state, sizeof(instance->device_state)); - instance->online = true; + hid_instance.os_driver->send_control(handle, activation_packet, sizeof(activation_packet)); + hid_instance.os_driver->read(handle, instance->device_state, sizeof(instance->device_state)); + instance->online = true; - RARCH_LOG("[gca]: init done\n"); - return instance; + RARCH_LOG("[gca]: init done\n"); + return instance; - error: - RARCH_ERR("[gca]: init failed\n"); - if(instance) - free(instance); - return NULL; + error: + RARCH_ERR("[gca]: init failed\n"); + if(instance) + free(instance); + return NULL; } static void wiiu_gca_free(void *data) { @@ -72,50 +81,90 @@ static void wiiu_gca_free(void *data) { instance->online = false; for(i = 0; i < 4; i++) - unregister_pad(instance, i); + unregister_pad(instance, i); free(instance); - } + } } static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size) { - wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; - if(!instance || !instance->online) - return; + wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; + if(!instance || !instance->online) + return; - if(size > sizeof(instance->device_state)) - return; + if(size > sizeof(instance->device_state)) + return; - memcpy(instance->device_state, buffer, size); - update_pad_state(instance); + memcpy(instance->device_state, buffer, size); + update_pad_state(instance); } static void update_pad_state(wiiu_gca_instance_t *instance) { - int i, pad; + int i, port; if(!instance || !instance->online) return; + joypad_connection_t *pad; /* process each pad */ for(i = 1; i < 37; i += 9) { - pad = i / 9; + port = i / 9; + pad = instance->pads[port]; + switch(instance->device_state[i]) { case GCA_PORT_INITIALIZING: case GCA_PORT_EMPTY: - if(instance->pads[pad] != NULL) - unregister_pad(instance, pad); + if(pad != NULL) { + RARCH_LOG("[gca]: Gamepad at port %d disconnected.\n", port+1); + unregister_pad(instance, port); + } break; case GCA_PORT_CONNECTED: - if(instance->pads[pad] == NULL) - instance->pads[pad] = register_pad(instance); + if(pad == NULL) + { + RARCH_LOG("[gca]: Gamepad at port %d connected.\n", port+1); + instance->pads[port] = register_pad(instance, port); + pad = instance->pads[port]; + if(pad == NULL) + { + RARCH_ERR("[gca]: Failed to register pad.\n"); + break; + } + } + + pad->iface->packet_handler(pad->data, &instance->device_state[i], 9); + break; } } } -static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance) { +/** + * This is kind of cheating because it's taking advantage of the fact that + * we know what the pad handle data format is. It'd be nice if the pad + * interface had a set_name function so this wouldn't be required. + */ +static void set_pad_name_for_port(gca_pad_t *pad, int port) +{ + char buf[45]; + + if(port >= 4) + return; + + snprintf(buf, 32, "Nintendo Gamecube Controller [GCA Port %d]", port); + if(pad->name) + { + free(pad->name); + pad->name = NULL; + } + + pad->name = strdup(buf); +} + + +static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port) { int slot; joypad_connection_t *result; @@ -129,6 +178,7 @@ static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance) { result = &(hid_instance.pad_list[slot]); result->iface = &wiiu_gca_pad_connection; result->data = result->iface->init(instance, slot, hid_instance.os_driver); + set_pad_name_for_port(result->data, port); result->connected = true; input_pad_connect(slot, hid_instance.pad_driver); @@ -159,8 +209,129 @@ hid_device_t wiiu_gca_hid_device = { "Wii U Gamecube Adapter" }; +/** + * Pad connection interface implementation. This handles each individual + * GC controller (as opposed to the above that handles the GCA itself). + */ + +static void *wiiu_gca_pad_init(void *data, uint32_t slot, hid_driver_t *driver) +{ + gca_pad_t *pad = (gca_pad_t *)calloc(1, sizeof(gca_pad_t)); + + if(!pad) + return NULL; + + memset(pad, 0, sizeof(gca_pad_t)); + + pad->gca_handle = data; + pad->driver = driver; + pad->slot = slot; + + return pad; +} + +static void wiiu_gca_pad_deinit(void *data) +{ + gca_pad_t *pad = (gca_pad_t *)data; + + if(pad) + { + if(pad->name) + free(pad->name); + + free(pad); + } +} + +static void wiiu_gca_get_buttons(void *data, retro_bits_t *state) +{ + gca_pad_t *pad = (gca_pad_t *)data; + if(pad) + { + BITS_COPY16_PTR(state, pad->buttons); + } else { + BIT256_CLEAR_ALL_PTR(state); + } +} + +static void wiiu_gca_packet_handler(void *data, uint8_t *packet, uint16_t size) +{ + gca_pad_t *pad = (gca_pad_t *)data; + uint32_t i, pressed_keys; + + static const uint32_t button_mapping[12] = + { + RETRO_DEVICE_ID_JOYPAD_A, + RETRO_DEVICE_ID_JOYPAD_B, + RETRO_DEVICE_ID_JOYPAD_X, + RETRO_DEVICE_ID_JOYPAD_Y, + RETRO_DEVICE_ID_JOYPAD_LEFT, + RETRO_DEVICE_ID_JOYPAD_RIGHT, + RETRO_DEVICE_ID_JOYPAD_DOWN, + RETRO_DEVICE_ID_JOYPAD_UP, + RETRO_DEVICE_ID_JOYPAD_START, + RETRO_DEVICE_ID_JOYPAD_SELECT, + RETRO_DEVICE_ID_JOYPAD_R, + RETRO_DEVICE_ID_JOYPAD_L, + }; + + if(!pad || !packet || size > sizeof(pad->data)) + return; + + memcpy(pad->data, packet, size); + pad->buttons = 0; + pressed_keys = pad->data[3] | (pad->data[4] << 8); + + for(i = 0; i < 12; i++) + { + pad->buttons |= (pressed_keys & (1 << i)) ? + (1 << button_mapping[i]) : 0; + } +} + +static void wiiu_gca_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) +{ + (void)data; + (void)effect; + (void)strength; +} + +static int16_t wiiu_gca_get_axis(void *data, unsigned axis) +{ + int16_t val; + gca_pad_t *pad = (gca_pad_t *)data; + + if(!pad || axis >= 4) + return 0; + + val = pad->data[5+axis]; + + switch(axis) + { + /* The Y axes are inverted. */ + case 0: /* left Y */ + case 2: /* right Y */ + val = 0x8000 - (val << 8); + break; + default: + val = (val << 8) - 0x8000; + break; + } + + if(val > 0x1000 || val < -0x1000) + return 0; + + return val; +} + +const char *wiiu_gca_get_name(void *data) +{ + gca_pad_t *pad = (gca_pad_t *)data; + + return pad->name; +} + pad_connection_interface_t wiiu_gca_pad_connection = { -/* wiiu_gca_pad_init, wiiu_gca_pad_deinit, wiiu_gca_packet_handler, @@ -168,5 +339,4 @@ pad_connection_interface_t wiiu_gca_pad_connection = { wiiu_gca_get_buttons, wiiu_gca_get_axis, wiiu_gca_get_name -*/ }; diff --git a/input/drivers/wiiu_input.c b/input/drivers/wiiu_input.c index 5a188c1f21..869e8f9d1b 100644 --- a/input/drivers/wiiu_input.c +++ b/input/drivers/wiiu_input.c @@ -33,7 +33,11 @@ #include "wiiu_dbg.h" +#ifdef WIIU_HID +#define MAX_PADS 16 +#else #define MAX_PADS 5 +#endif static uint8_t keyboardChannel = 0x00; static bool keyboardState[RETROK_LAST] = { 0 }; diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index aa8b78d030..1e4efb0049 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -555,9 +555,7 @@ static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, int16_t r2 = ((error & 0xFFFF0000) >> 16); RARCH_ERR("[hid]: read failed: %08x (%d:%d)\n", error, r2, r1); } else { -#if 0 adapter->driver->handle_packet(adapter->driver_handle, buffer, buffer_size); -#endif } } From 6b43defc983b501e8be3ebe7290de67ebffdcaf4 Mon Sep 17 00:00:00 2001 From: gblues Date: Sun, 25 Mar 2018 23:20:17 -0700 Subject: [PATCH 082/517] Less verbose logging --- wiiu/input/wiiu_hid.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index 1e4efb0049..b9cc8c3af4 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -93,7 +93,6 @@ static void *wiiu_hid_init(hid_driver_instance_t *driver) if (!hid->polling_thread) goto error; - RARCH_LOG("[hid]: Registering HIDClient\n"); HIDAddClient(client, wiiu_attach_callback); hid->client = client; @@ -216,13 +215,7 @@ static int32_t wiiu_hid_read(void *data, void *buffer, size_t size) if(size > adapter->rx_size) return -1; -#if 1 - int32_t result = HIDRead(adapter->handle, buffer, size, NULL, NULL); - log_buffer(buffer, size); - return result; -#else return HIDRead(adapter->handle, buffer, size, NULL, NULL); -#endif } @@ -409,11 +402,10 @@ static int32_t wiiu_attach_callback(HIDClient *client, { wiiu_attach_event *event = NULL; - log_device(device); - switch(attach) { case HID_DEVICE_ATTACH: + log_device(device); case HID_DEVICE_DETACH: if (device) event = new_attach_event(device); @@ -459,10 +451,7 @@ static void wiiu_hid_attach(wiiu_hid_t *hid, wiiu_attach_event *event) adapter->driver = event->driver; adapter->state = ADAPTER_STATE_NEW; - RARCH_LOG("[hid]: adding to adapter list\n"); synchronized_add_to_adapters_list(adapter); - - RARCH_LOG("[hid]: starting read loop\n"); wiiu_start_read_loop(adapter); return; From 1eea48d0c8ecfc3bd98b4246f3dccaaa5ec0b74a Mon Sep 17 00:00:00 2001 From: gblues Date: Mon, 26 Mar 2018 23:35:54 -0700 Subject: [PATCH 083/517] Fix crash on exit bug == DETAILS Turns out freeing memory that's already been freed is.. bad. Fix two double-free instances; one due to over-freeing and the other due to wrong order-of-operations causing a double free. Also updated logging a little. == TESTING The GC adapter still clobbers slot 0, but the "emergency exit" sequence works to quit RA cleanly. --- input/common/hid/device_wiiu_gca.c | 10 ++++++++++ input/common/hid/hid_device_driver.c | 12 +++++++++++- wiiu/input/wiiu_hid.c | 7 ++++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 79fb2ae59d..2cfb601771 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -169,12 +169,19 @@ static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port joypad_connection_t *result; if(!instance || !instance->online) + { + RARCH_ERR("[gca]: bad instance\n"); return NULL; + } slot = pad_connection_find_vacant_pad(hid_instance.pad_list); if(slot < 0) + { + RARCH_ERR("[gca]: failed to find a free slot\n"); return NULL; + } + RARCH_LOG("[gca]: registering pad in port %d to slot %d\n", port+1, slot); result = &(hid_instance.pad_list[slot]); result->iface = &wiiu_gca_pad_connection; result->data = result->iface->init(instance, slot, hid_instance.os_driver); @@ -237,7 +244,10 @@ static void wiiu_gca_pad_deinit(void *data) if(pad) { if(pad->name) + { free(pad->name); + pad->name = NULL; + } free(pad); } diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c index e850188f07..13bbf89d40 100644 --- a/input/common/hid/hid_device_driver.c +++ b/input/common/hid/hid_device_driver.c @@ -58,13 +58,16 @@ bool hid_init(hid_driver_instance_t *instance, input_device_driver_t *pad_driver, unsigned slots) { + RARCH_LOG("[hid]: initializing instance with %d pad slots\n", slots); if(!instance || !hid_driver || !pad_driver || slots > MAX_USERS) return false; + RARCH_LOG("[hid]: initializing HID subsystem driver...\n"); instance->os_driver_data = hid_driver->init(instance); if(!instance->os_driver_data) return false; + RARCH_LOG("[hid]: initializing pad list...\n"); instance->pad_list = pad_connection_init(slots); if(!instance->pad_list) { @@ -77,6 +80,8 @@ bool hid_init(hid_driver_instance_t *instance, instance->os_driver = hid_driver; instance->pad_driver = pad_driver; + RARCH_LOG("[hid]: instance initialization complete.\n"); + return true; } @@ -90,13 +95,18 @@ void hid_deinit(hid_driver_instance_t *instance) if(!instance) return; - pad_connection_destroy(instance->pad_list); + RARCH_LOG("[hid]: destroying instance\n"); if(instance->os_driver && instance->os_driver_data) { + RARCH_LOG("[hid]: tearing down HID subsystem driver...\n"); instance->os_driver->free(instance->os_driver_data); } + RARCH_LOG("[hid]: destroying pad data...\n"); + pad_connection_destroy(instance->pad_list); + + RARCH_LOG("[hid]: wiping instance data...\n"); memset(instance, 0, sizeof(hid_driver_instance_t)); } diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index b9cc8c3af4..7bbca4987e 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -430,8 +430,7 @@ static void wiiu_hid_detach(wiiu_hid_t *hid, wiiu_attach_event *event) wiiu_adapter_t *adapter = synchronized_remove_from_adapters_list(event->handle); if(adapter) { - adapter->driver->free(adapter->driver_handle); - adapter->driver_handle = NULL; + RARCH_LOG("[hid]: freeing detached pad\n"); delete_adapter(adapter); } } @@ -580,8 +579,10 @@ static void wiiu_hid_polling_thread_cleanup(OSThread *thread, void *stack) * while we are holding the lock. */ if(incomplete == 0) { + RARCH_LOG("All in-flight reads complete.\n"); while(adapters.list != NULL) { + RARCH_LOG("[hid]: shutting down adapter..\n"); adapter = adapters.list; adapters.list = adapter->next; delete_adapter(adapter); @@ -599,7 +600,6 @@ static void wiiu_hid_polling_thread_cleanup(OSThread *thread, void *stack) } }while(incomplete); - RARCH_LOG("All in-flight reads complete.\n"); } static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list) @@ -725,6 +725,7 @@ static void delete_adapter(wiiu_adapter_t *adapter) adapter->driver_handle = NULL; adapter->driver = NULL; } + free(adapter); } From 89c1ba7929c78dfbf828a2d07e6c3d2d75fdacb0 Mon Sep 17 00:00:00 2001 From: gblues Date: Wed, 28 Mar 2018 00:02:09 -0700 Subject: [PATCH 084/517] Keep HID pads from clobbering gamepad/wiimotes == DETAILS Trying to do weird pad math just wasn't working so I bit the bullet and just let it allocate all 16 pads in the slot list, then just mark 0-4 as connected so that the slot allocator would start at 5. I can see it detect the pad, but no idea if it works. Out of time for today. --- input/common/hid/device_wiiu_gca.c | 41 ++++++------------------------ input/input_autodetect_builtin.c | 1 + wiiu/input/hidpad_driver.c | 26 ++++++++++--------- wiiu/input/wiiu_hid.c | 6 +---- 4 files changed, 24 insertions(+), 50 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 2cfb601771..6cda78cad2 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -40,14 +40,12 @@ typedef struct gca_pad_data uint8_t data[9]; // pad data uint32_t slot; // slot this pad occupies uint32_t buttons; // digital button state - char *name; // name of the pad } gca_pad_t; static void update_pad_state(wiiu_gca_instance_t *instance); static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port); static void unregister_pad(wiiu_gca_instance_t *instance, int port); -static void set_pad_name_for_port(gca_pad_t *pad, int port); extern pad_connection_interface_t wiiu_gca_pad_connection; @@ -91,10 +89,17 @@ static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size) { wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; if(!instance || !instance->online) + { + RARCH_WARN("[gca]: instance null or not ready yet.\n"); return; + } if(size > sizeof(instance->device_state)) + { + RARCH_WARN("[gca]: packet size %d is too big for buffer of size %d\n", + size, sizeof(instance->device_state)); return; + } memcpy(instance->device_state, buffer, size); update_pad_state(instance); @@ -141,29 +146,6 @@ static void update_pad_state(wiiu_gca_instance_t *instance) } } -/** - * This is kind of cheating because it's taking advantage of the fact that - * we know what the pad handle data format is. It'd be nice if the pad - * interface had a set_name function so this wouldn't be required. - */ -static void set_pad_name_for_port(gca_pad_t *pad, int port) -{ - char buf[45]; - - if(port >= 4) - return; - - snprintf(buf, 32, "Nintendo Gamecube Controller [GCA Port %d]", port); - if(pad->name) - { - free(pad->name); - pad->name = NULL; - } - - pad->name = strdup(buf); -} - - static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port) { int slot; joypad_connection_t *result; @@ -185,7 +167,6 @@ static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port result = &(hid_instance.pad_list[slot]); result->iface = &wiiu_gca_pad_connection; result->data = result->iface->init(instance, slot, hid_instance.os_driver); - set_pad_name_for_port(result->data, port); result->connected = true; input_pad_connect(slot, hid_instance.pad_driver); @@ -243,12 +224,6 @@ static void wiiu_gca_pad_deinit(void *data) if(pad) { - if(pad->name) - { - free(pad->name); - pad->name = NULL; - } - free(pad); } } @@ -338,7 +313,7 @@ const char *wiiu_gca_get_name(void *data) { gca_pad_t *pad = (gca_pad_t *)data; - return pad->name; + return "GameCube Controller"; } pad_connection_interface_t wiiu_gca_pad_connection = { diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index 0e76b04f4e..a6096f2d87 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -612,6 +612,7 @@ const char* const input_builtin_autoconfs[] = DECL_AUTOCONF_DEVICE(PAD_NAME_NUNCHUK, "wiiu", WIIUINPUT_NUNCHUK_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE(PAD_NAME_CLASSIC, "wiiu", WIIUINPUT_CLASSIC_CONTROLLER_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE(PAD_NAME_HID, "wiiu", WIIUINPUT_GAMEPAD_DEFAULT_BINDS), + DECL_AUTOCONF_DEVICE("GameCube Controller", "wiiu", WIIUINPUT_GAMEPAD_DEFAULT_BINDS), #endif #ifdef __CELLOS_LV2__ DECL_AUTOCONF_DEVICE("SixAxis Controller", "ps3", PS3INPUT_DEFAULT_BINDS), diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c index 8991d156ba..8db27a66dc 100644 --- a/wiiu/input/hidpad_driver.c +++ b/wiiu/input/hidpad_driver.c @@ -29,23 +29,17 @@ static const char *hidpad_name(unsigned pad); static bool ready = false; -static unsigned to_slot(unsigned pad) -{ - return pad - (WIIU_WIIMOTE_CHANNELS+1); -} - static bool init_hid_driver(void) { - unsigned connections_size = MAX_USERS - (WIIU_WIIMOTE_CHANNELS+1); - memset(&hid_instance, 0, sizeof(hid_instance)); - return hid_init(&hid_instance, &wiiu_hid, &hidpad_driver, connections_size); + return hid_init(&hid_instance, &wiiu_hid, &hidpad_driver, MAX_USERS); } static bool hidpad_init(void *data) { (void *)data; + int i; if(!init_hid_driver()) { @@ -53,6 +47,14 @@ static bool hidpad_init(void *data) return false; } +/* hid_instance.pad_list[0].connected = true; */ + + for(i = 0; i < (WIIU_WIIMOTE_CHANNELS+1); i++) + { + hid_instance.pad_list[i].connected = true; + } + + hidpad_poll(); ready = true; @@ -77,7 +79,7 @@ static bool hidpad_button(unsigned pad, uint16_t button) return false; #if 0 - return hid_driver->button(hid_data, to_slot(pad), button); + return hid_driver->button(hid_data, pad, button); #else return false; #endif @@ -89,7 +91,7 @@ static void hidpad_get_buttons(unsigned pad, retro_bits_t *state) BIT256_CLEAR_ALL_PTR(state); #if 0 - hid_driver->get_buttons(hid_data, to_slot(pad), state); + hid_driver->get_buttons(hid_data, pad, state); #endif BIT256_CLEAR_ALL_PTR(state); } @@ -100,7 +102,7 @@ static int16_t hidpad_axis(unsigned pad, uint32_t axis) return 0; #if 0 - return hid_driver->axis(hid_data, to_slot(pad), axis); + return hid_driver->axis(hid_data, pad, axis); #else return 0; #endif @@ -120,7 +122,7 @@ static const char *hidpad_name(unsigned pad) #if 1 return PAD_NAME_HID; #else - return hid_driver->name(hid_data, to_slot(pad)); + return hid_driver->name(hid_data, pad); #endif } diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index 7bbca4987e..d6a1a6c707 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -328,16 +328,12 @@ static void synchronized_process_adapters(wiiu_hid_t *hid) { switch(adapter->state) { - case ADAPTER_STATE_DONE: - break; case ADAPTER_STATE_NEW: adapter->state = try_init_driver(adapter); break; case ADAPTER_STATE_READY: case ADAPTER_STATE_READING: -#if 0 - adapter->driver->poll(); -#endif + case ADAPTER_STATE_DONE: break; default: RARCH_ERR("[hid]: Invalid adapter state: %d\n", adapter->state); From 5060c2aac473cb8b617b03d83033363f60d09c14 Mon Sep 17 00:00:00 2001 From: gblues Date: Thu, 29 Mar 2018 23:33:31 -0700 Subject: [PATCH 085/517] More fixes, GC pad kinda sorta works == DETAILS - Added a new method to the joypad_connection_t interface for getting a single button - wired everything into the hidpad driver - for testing purposes, hacking the top-level joypad driver so that kpad isn't used - add a new RARCH_LOG_BUFFER method to verbosity for logging the contents of a binary buffer (useful for writing/debugging pad drivers) - fix a few bugs in the wiiu GC pad driver The button mapping isn't quite right, and I'm not sure what's going wrong. --- input/common/hid/device_wiiu_gca.c | 48 +++++++++++-- input/connect/joypad_connection.h | 1 + input/drivers_joypad/wiiu_joypad.c | 9 +-- input/include/hid_driver.h | 13 ++++ input/input_autodetect_builtin.c | 14 ++++ verbosity.c | 31 ++++++++ verbosity.h | 11 +-- wiiu/input/hidpad_driver.c | 33 +++------ wiiu/input/wiiu_hid.c | 110 ++++++++++------------------- 9 files changed, 160 insertions(+), 110 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 6cda78cad2..7556305f6d 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -101,6 +101,8 @@ static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size) return; } + //RARCH_LOG_BUFFER(buffer, size); + memcpy(instance->device_state, buffer, size); update_pad_state(instance); } @@ -239,6 +241,29 @@ static void wiiu_gca_get_buttons(void *data, retro_bits_t *state) } } +static void log_bitmask(uint32_t bits) +{ + char buf[33]; + int i; + + for(i = 0; i < 32; i++) + { + buf[i] = (bits & (1 << i)) ? '1' : '0'; + } + buf[32] = '\0'; + + RARCH_LOG("pressed_keys: %s\n", buf); +} + +/** + * The USB packet provides a 9-byte data packet for each pad. + * + * byte 0: connection status (0x14 = connected, 0x04 = disconnected) + * bytes 1-2: digital buttons + * bytes 3-4: left analog stick x/y + * bytes 5-6: right analog stick x/y + * bytes 7-8: L/R analog state (note that these have digital buttons too) + */ static void wiiu_gca_packet_handler(void *data, uint8_t *packet, uint16_t size) { gca_pad_t *pad = (gca_pad_t *)data; @@ -263,9 +288,13 @@ static void wiiu_gca_packet_handler(void *data, uint8_t *packet, uint16_t size) if(!pad || !packet || size > sizeof(pad->data)) return; +/* RARCH_LOG_BUFFER(packet, size); */ + memcpy(pad->data, packet, size); pad->buttons = 0; - pressed_keys = pad->data[3] | (pad->data[4] << 8); + pressed_keys = pad->data[1] | (pad->data[2] << 8); + + log_bitmask(pressed_keys); for(i = 0; i < 12; i++) { @@ -289,7 +318,7 @@ static int16_t wiiu_gca_get_axis(void *data, unsigned axis) if(!pad || axis >= 4) return 0; - val = pad->data[5+axis]; + val = pad->data[3+axis]; switch(axis) { @@ -309,13 +338,23 @@ static int16_t wiiu_gca_get_axis(void *data, unsigned axis) return val; } -const char *wiiu_gca_get_name(void *data) +static const char *wiiu_gca_get_name(void *data) { gca_pad_t *pad = (gca_pad_t *)data; return "GameCube Controller"; } +static bool wiiu_gca_button(void *data, uint16_t joykey) +{ + gca_pad_t *pad = (gca_pad_t *)data; + + if(!pad) + return false; + + return (pad->buttons & joykey); +} + pad_connection_interface_t wiiu_gca_pad_connection = { wiiu_gca_pad_init, wiiu_gca_pad_deinit, @@ -323,5 +362,6 @@ pad_connection_interface_t wiiu_gca_pad_connection = { wiiu_gca_set_rumble, wiiu_gca_get_buttons, wiiu_gca_get_axis, - wiiu_gca_get_name + wiiu_gca_get_name, + wiiu_gca_button }; diff --git a/input/connect/joypad_connection.h b/input/connect/joypad_connection.h index ec78b3c4df..e2f421dd45 100644 --- a/input/connect/joypad_connection.h +++ b/input/connect/joypad_connection.h @@ -61,6 +61,7 @@ typedef struct pad_connection_interface void (*get_buttons)(void *data, retro_bits_t *state); int16_t (*get_axis)(void *data, unsigned axis); const char* (*get_name)(void *data); + bool (*button)(void *data, uint16_t joykey); } pad_connection_interface_t; typedef struct joypad_connection joypad_connection_t; diff --git a/input/drivers_joypad/wiiu_joypad.c b/input/drivers_joypad/wiiu_joypad.c index 4ae4a4863a..aa9c3ab277 100644 --- a/input/drivers_joypad/wiiu_joypad.c +++ b/input/drivers_joypad/wiiu_joypad.c @@ -40,9 +40,10 @@ static input_device_driver_t *get_driver_for_pad(unsigned pad) { if(wpad_driver.query_pad(pad)) return &wpad_driver; +/* if(kpad_driver.query_pad(pad)) return &kpad_driver; - +*/ #ifdef WIIU_HID return &hidpad_driver; #else @@ -84,11 +85,7 @@ static bool wiiu_joypad_init(void* data) static bool wiiu_joypad_query_pad(unsigned pad) { -#ifdef WIIU_HID return ready && pad < MAX_USERS; -#else - return ready && pad < 5; -#endif } static void wiiu_joypad_destroy(void) @@ -138,7 +135,7 @@ static void wiiu_joypad_poll(void) static const char* wiiu_joypad_name(unsigned pad) { if(!wiiu_joypad_query_pad(pad)) - return "N/A"; + return "Snuffleupagus"; return pad_drivers[pad]->name(pad); } diff --git a/input/include/hid_driver.h b/input/include/hid_driver.h index 59b5a62d28..1eee8a3f85 100644 --- a/input/include/hid_driver.h +++ b/input/include/hid_driver.h @@ -52,6 +52,19 @@ struct hid_driver int32_t (*read)(void *handle, void *buf, size_t size); }; +#define HID_GET_BUTTONS(pad, state) hid_instance.os_driver->get_buttons( \ + hid_instance.os_driver_data, pad, state) +#define HID_BUTTON(pad, key) hid_instance.os_driver->button( \ + hid_instance.os_driver_data, pad, key) +#define HID_AXIS(pad, axis) hid_instance.os_driver->axis( \ + hid_instance.os_driver_data, pad, axis) +#define HID_PAD_NAME(pad) hid_instance.os_driver->name( \ + hid_instance.os_driver_data, pad) +#define HID_POLL() hid_instance.os_driver->poll( \ + hid_instance.os_driver_data) + + + struct hid_driver_instance { hid_driver_t *os_driver; void *os_driver_data; diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index a6096f2d87..45bbed0775 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -228,6 +228,20 @@ DECL_AXIS(r_y_minus, +3) #ifdef WIIU +#define WIIUINPUT_GAMECUBE_DEFAULT_BINDS \ +DECL_BTN_EX(b, 0, "B") \ +DECL_BTN_EX(y, 1, "Y") \ +DECL_BTN_EX(select, 2, "Z") \ +DECL_BTN_EX(start, 3, "Start/Pause") \ +DECL_BTN_EX(up, 4, "D-Pad Up") \ +DECL_BTN_EX(down, 5, "D-Pad Down") \ +DECL_BTN_EX(left, 6, "D-Pad Left") \ +DECL_BTN_EX(right, 7, "D-Pad Right") \ +DECL_BTN_EX(a, 8, "A") \ +DECL_BTN_EX(x, 9, "X") \ +DECL_BTN_EX(l, 10, "L") \ +DECL_BTN_EX(r, 11, "R") + #define WIIUINPUT_GAMEPAD_DEFAULT_BINDS \ DECL_BTN_EX(menu_toggle, 1, "Home") \ DECL_BTN_EX(select, 2, "-") \ diff --git a/verbosity.c b/verbosity.c index ddb46512a9..ee6837132c 100644 --- a/verbosity.c +++ b/verbosity.c @@ -193,6 +193,37 @@ void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap) #endif } +void RARCH_LOG_BUFFER(uint8_t *data, size_t size) +{ + int i, offset; + int padding = size % 16; + uint8_t buf[16]; + + RARCH_LOG("== %d-byte buffer ==================\n", size); + for(i = 0, offset = 0; i < size; i++) + { + buf[offset] = data[i]; + offset++; + + if(offset == 16) + { + offset = 0; + RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + } + if(padding) + { + for(i = padding; i < 16; i++) + buf[i] = 0xff; + RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + RARCH_LOG("==================================\n"); +} + void RARCH_LOG(const char *fmt, ...) { va_list ap; diff --git a/verbosity.h b/verbosity.h index 5f5cdcf30a..42ae964f58 100644 --- a/verbosity.h +++ b/verbosity.h @@ -86,7 +86,7 @@ void logger_send_v(const char *__format, va_list args); logger_send_v(fmt, vp); \ } while (0) -#else +#else /* IS_SALAMANDER */ #define RARCH_LOG(...) do { \ logger_send("" __VA_ARGS__); \ @@ -123,11 +123,14 @@ void logger_send_v(const char *__format, va_list args); logger_send("[OUTPUT] " tag); \ logger_send_v(fmt, vp); \ } while (0) - #endif -#else + +#define RARCH_LOG_BUFFER(...) do { } while(0) + +#else /* HAVE_LOGGER */ void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap); void RARCH_LOG(const char *fmt, ...); +void RARCH_LOG_BUFFER(uint8_t *buffer, size_t size); void RARCH_LOG_OUTPUT(const char *msg, ...); void RARCH_WARN(const char *fmt, ...); void RARCH_ERR(const char *fmt, ...); @@ -135,7 +138,7 @@ void RARCH_ERR(const char *fmt, ...); #define RARCH_LOG_OUTPUT_V RARCH_LOG_V #define RARCH_WARN_V RARCH_LOG_V #define RARCH_ERR_V RARCH_LOG_V -#endif +#endif /* HAVE_LOGGER */ RETRO_END_DECLS diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c index 8db27a66dc..97f05e9d06 100644 --- a/wiiu/input/hidpad_driver.c +++ b/wiiu/input/hidpad_driver.c @@ -47,13 +47,13 @@ static bool hidpad_init(void *data) return false; } -/* hid_instance.pad_list[0].connected = true; */ - + hid_instance.pad_list[0].connected = true; +/* for(i = 0; i < (WIIU_WIIMOTE_CHANNELS+1); i++) { hid_instance.pad_list[i].connected = true; } - +*/ hidpad_poll(); ready = true; @@ -63,7 +63,7 @@ static bool hidpad_init(void *data) static bool hidpad_query_pad(unsigned pad) { - return ready && (pad > WIIU_WIIMOTE_CHANNELS && pad < MAX_USERS); + return ready && pad < MAX_USERS; } static void hidpad_destroy(void) @@ -78,11 +78,7 @@ static bool hidpad_button(unsigned pad, uint16_t button) if (!hidpad_query_pad(pad)) return false; -#if 0 - return hid_driver->button(hid_data, pad, button); -#else - return false; -#endif + return HID_BUTTON(pad, button); } static void hidpad_get_buttons(unsigned pad, retro_bits_t *state) @@ -90,10 +86,7 @@ static void hidpad_get_buttons(unsigned pad, retro_bits_t *state) if (!hidpad_query_pad(pad)) BIT256_CLEAR_ALL_PTR(state); -#if 0 - hid_driver->get_buttons(hid_data, pad, state); -#endif - BIT256_CLEAR_ALL_PTR(state); + HID_GET_BUTTONS(pad, state); } static int16_t hidpad_axis(unsigned pad, uint32_t axis) @@ -101,17 +94,13 @@ static int16_t hidpad_axis(unsigned pad, uint32_t axis) if (!hidpad_query_pad(pad)); return 0; -#if 0 - return hid_driver->axis(hid_data, pad, axis); -#else - return 0; -#endif + return HID_AXIS(pad, axis); } static void hidpad_poll(void) { if (ready) - hid_instance.os_driver->poll(hid_instance.os_driver_data); + HID_POLL(); } static const char *hidpad_name(unsigned pad) @@ -119,11 +108,7 @@ static const char *hidpad_name(unsigned pad) if (!hidpad_query_pad(pad)) return "N/A"; -#if 1 - return PAD_NAME_HID; -#else - return hid_driver->name(hid_data, pad); -#endif + return HID_PAD_NAME(pad); } input_device_driver_t hidpad_driver = diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index d6a1a6c707..44c2822305 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -19,8 +19,6 @@ static wiiu_event_list events; static wiiu_adapter_list adapters; -static void log_buffer(uint8_t *data, uint32_t len); - static bool wiiu_hid_joypad_query(void *data, unsigned slot) { wiiu_hid_t *hid = (wiiu_hid_t *)data; @@ -30,51 +28,66 @@ static bool wiiu_hid_joypad_query(void *data, unsigned slot) return slot < hid->driver->max_slot; } -static const char *wiiu_hid_joypad_name(void *data, unsigned slot) +static joypad_connection_t *get_pad(wiiu_hid_t *hid, unsigned slot) { - if (!wiiu_hid_joypad_query(data, slot)) + if(!wiiu_hid_joypad_query(hid, slot)) return NULL; - wiiu_hid_t *hid = (wiiu_hid_t *)data; + joypad_connection_t *result = &(hid->driver->pad_list[slot]); + if(!result || !result->connected || !result->iface || !result->data) + return NULL; - return hid->driver->pad_list[slot].iface->get_name(data); + return result; } -static void wiiu_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state) +static const char *wiiu_hid_joypad_name(void *data, unsigned slot) { - (void)data; - (void)port; + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); - BIT256_CLEAR_ALL_PTR(state); + if(!pad) + return NULL; + + return pad->iface->get_name(pad->data); } -static bool wiiu_hid_joypad_button(void *data, unsigned port, uint16_t joykey) +static void wiiu_hid_joypad_get_buttons(void *data, unsigned slot, retro_bits_t *state) { - (void)data; - (void)port; - (void)joykey; + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); - return false; + if(pad) + pad->iface->get_buttons(pad->data, state); } -static bool wiiu_hid_joypad_rumble(void *data, unsigned pad, +static bool wiiu_hid_joypad_button(void *data, unsigned slot, uint16_t joykey) +{ + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); + + if(!pad) + return false; + + return pad->iface->button(pad->data, joykey); +} + +static bool wiiu_hid_joypad_rumble(void *data, unsigned slot, enum retro_rumble_effect effect, uint16_t strength) { - (void)data; - (void)pad; - (void)effect; - (void)strength; + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); + if(!pad) + return false; + + pad->iface->set_rumble(pad->data, effect, strength); return false; } -static int16_t wiiu_hid_joypad_axis(void *data, unsigned port, uint32_t joyaxis) +static int16_t wiiu_hid_joypad_axis(void *data, unsigned slot, uint32_t joyaxis) { - (void)data; - (void)port; - (void)joyaxis; + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); - return 0; + if(!pad) + return 0; + + return pad->iface->get_axis(pad->data, joyaxis); } static void *wiiu_hid_init(hid_driver_instance_t *driver) @@ -464,53 +477,6 @@ void wiiu_start_read_loop(wiiu_adapter_t *adapter) adapter); } -/** - * Takes a buffer and formats it for the log file, 16 bytes per line. - * - * When the end of the buffer is reached, it is padded out with 0xff. So e.g. - * a 5-byte buffer might look like: - * - * 5 bytes read fro HIDRead: - * 0102030405ffffff ffffffffffffffff - * ================================== - */ -static void log_buffer(uint8_t *data, uint32_t len) -{ - int i, o; - int padding = len % 16; - uint8_t buf[16]; - - (uint8_t *)data; - (uint32_t)len; - - RARCH_LOG("%d bytes read from HIDRead:\n", len); - - for(i = 0, o = 0; i < len; i++) - { - buf[o] = data[i]; - o++; - if(o == 16) - { - o = 0; - RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); - } - } - - if(padding) - { - for(i = padding; i < 16; i++) - buf[i] = 0xff; - - RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); - } - RARCH_LOG("==================================\n"); - -} - static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, uint8_t *buffer, uint32_t buffer_size, void *userdata) { From 82e2cc7c73a1f8b0693cdfec59e04665e99375c4 Mon Sep 17 00:00:00 2001 From: ceb33 Date: Fri, 30 Mar 2018 10:43:54 +0200 Subject: [PATCH 086/517] set hid device registration deterministic (sorting by ascending location_id), this solve the issue where game with same vid and pid where sometime swapped by the OS --- input/drivers_hid/iohidmanager_hid.c | 91 ++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 19 deletions(-) diff --git a/input/drivers_hid/iohidmanager_hid.c b/input/drivers_hid/iohidmanager_hid.c index 9ad99ebe2f..338ea7e51f 100644 --- a/input/drivers_hid/iohidmanager_hid.c +++ b/input/drivers_hid/iohidmanager_hid.c @@ -474,6 +474,12 @@ static uint16_t iohidmanager_hid_device_get_product_id(IOHIDDeviceRef device) CFSTR(kIOHIDProductIDKey)); } +static uint32_t iohidmanager_hid_device_get_location_id(IOHIDDeviceRef device) +{ + return iohidmanager_hid_device_get_int_property(device, + CFSTR(kIOHIDLocationIDKey)); +} + static void iohidmanager_hid_device_get_product_string( IOHIDDeviceRef device, char *buf, size_t len) { @@ -501,8 +507,8 @@ static void iohidmanager_hid_device_add_autodetect(unsigned idx, RARCH_LOG("Port %d: %s.\n", idx, device_name); } -static void iohidmanager_hid_device_add(void *data, IOReturn result, - void* sender, IOHIDDeviceRef device) +static void iohidmanager_hid_device_add(IOHIDDeviceRef device, iohidmanager_hid_t* hid) + { int i; IOReturn ret; @@ -516,8 +522,6 @@ static void iohidmanager_hid_device_add(void *data, IOReturn result, apple_input_rec_t *tmp = NULL; apple_input_rec_t *tmpButtons = NULL; apple_input_rec_t *tmpAxes = NULL; - iohidmanager_hid_t *hid = (iohidmanager_hid_t*) - hid_driver_get_data(); struct iohidmanager_hid_adapter *adapter = (struct iohidmanager_hid_adapter*) calloc(1, sizeof(*adapter)); @@ -832,24 +836,73 @@ static int iohidmanager_hid_manager_free(iohidmanager_hid_t *hid) static int iohidmanager_hid_manager_set_device_matching( iohidmanager_hid_t *hid) { - CFMutableArrayRef matcher = CFArrayCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeArrayCallBacks); + CFSetRef set = IOHIDManagerCopyDevices(hid->ptr); + CFIndex num_devices = CFSetGetCount(set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(set, (const void **) device_array); - if (!matcher) - return -1; + /* re order device by location id */ + typedef struct hid_list + { + IOHIDDeviceRef device; + uint32_t lid; + struct hid_list *next; + } hid_list_t; + + hid_list_t* devList = NULL; + for (long i=0; idevice = dev; + devList->lid = iohidmanager_hid_device_get_location_id(dev); + //printf("%d\n",devList->lid); + devList->next = NULL; + } + else + { + hid_list_t * new = (hid_list_t *)malloc(sizeof(hid_list_t)); + new->device = dev; + new->lid = iohidmanager_hid_device_get_location_id(dev); + //printf("%d\n",new->lid); + new->next = NULL; - iohidmanager_hid_append_matching_dictionary(matcher, - kHIDPage_GenericDesktop, - kHIDUsage_GD_Joystick); - iohidmanager_hid_append_matching_dictionary(matcher, - kHIDPage_GenericDesktop, - kHIDUsage_GD_GamePad); + hid_list_t * ptr = devList; + if ( new->lid < ptr->lid ) + { + new->next = ptr; + devList = new; + } + else + { + while ( ( ptr->lid < new->lid ) && (ptr->next != NULL) ) + { + ptr = ptr->next; + } + new->next = ptr->next; + ptr->next = new; + } + } + } + } - IOHIDManagerSetDeviceMatchingMultiple(hid->ptr, matcher); - IOHIDManagerRegisterDeviceMatchingCallback(hid->ptr, - iohidmanager_hid_device_add, 0); - - CFRelease(matcher); + /* register devices */ + hid_list_t * ptr = devList; + while (ptr != NULL) + { + iohidmanager_hid_device_add(ptr->device, hid); + //printf("%d\n",ptr->lid); + ptr = ptr->next; + free(devList); + devList = ptr; + } return 0; } From c6d6fc7098513dce0c96bc729a7074d5ce07aff2 Mon Sep 17 00:00:00 2001 From: ceb33 Date: Fri, 30 Mar 2018 11:00:38 +0200 Subject: [PATCH 087/517] fix memory leak --- input/drivers_hid/iohidmanager_hid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/input/drivers_hid/iohidmanager_hid.c b/input/drivers_hid/iohidmanager_hid.c index 338ea7e51f..3d545ccc82 100644 --- a/input/drivers_hid/iohidmanager_hid.c +++ b/input/drivers_hid/iohidmanager_hid.c @@ -903,6 +903,7 @@ static int iohidmanager_hid_manager_set_device_matching( free(devList); devList = ptr; } + free(device_array); return 0; } From 94254e4c790113e63e760fedd5d9e9940279a7c0 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 30 Mar 2018 11:49:25 +0200 Subject: [PATCH 088/517] (IOHIDManager) Cleanups --- input/drivers_hid/iohidmanager_hid.c | 242 ++++++++++++++------------- 1 file changed, 126 insertions(+), 116 deletions(-) diff --git a/input/drivers_hid/iohidmanager_hid.c b/input/drivers_hid/iohidmanager_hid.c index 3d545ccc82..b85a3c1117 100644 --- a/input/drivers_hid/iohidmanager_hid.c +++ b/input/drivers_hid/iohidmanager_hid.c @@ -70,7 +70,7 @@ CFComparisonResult iohidmanager_sort_elements(const void *val1, const void *val2 if (page1 != page2) return (CFComparisonResult)(page1 > page2); - if(use1 != use2) + if (use1 != use2) return (CFComparisonResult)(use1 > use2); return (CFComparisonResult)(cookie1 > cookie2); @@ -78,9 +78,9 @@ CFComparisonResult iohidmanager_sort_elements(const void *val1, const void *val2 static bool iohidmanager_check_for_id(apple_input_rec_t *rec, uint32_t id) { - while(rec) + while (rec) { - if(rec->id == id) + if (rec->id == id) return true; rec = rec->next; } @@ -90,7 +90,7 @@ static bool iohidmanager_check_for_id(apple_input_rec_t *rec, uint32_t id) static void iohidmanager_append_record(apple_input_rec_t *rec, apple_input_rec_t *b) { apple_input_rec_t *tmp = rec; - while(tmp->next) + while (tmp->next) tmp = tmp->next; tmp->next = b; } @@ -110,12 +110,13 @@ static void iohidmanager_append_record(apple_input_rec_t *rec, apple_input_rec_t static void iohidmanager_append_record_ordered(apple_input_rec_t **p_rec, apple_input_rec_t *b) { apple_input_rec_t *tmp = *p_rec; - while(tmp && (tmp->id <= b->id)) { - p_rec = &tmp->next; - tmp = tmp->next; + while (tmp && (tmp->id <= b->id)) + { + p_rec = &tmp->next; + tmp = tmp->next; } - b->next = tmp; - *p_rec = b; + b->next = tmp; + *p_rec = b; } static bool iohidmanager_hid_joypad_query(void *data, unsigned pad) @@ -154,7 +155,7 @@ static bool iohidmanager_hid_joypad_button(void *data, if (hat_dir) { unsigned h = GET_HAT(joykey); - if(h >= 1) + if (h >= 1) return false; switch(hat_dir) @@ -207,7 +208,7 @@ static int16_t iohidmanager_hid_joypad_axis(void *data, if (val >= 0) val = 0; } - else if(AXIS_POS_GET(joyaxis) < 6) + else if (AXIS_POS_GET(joyaxis) < 6) { val += hid->axes[port][AXIS_POS_GET(joyaxis)]; val += pad_connection_get_axis(&hid->slots[port], @@ -282,15 +283,15 @@ static void iohidmanager_hid_device_input_callback(void *data, IOReturn result, { tmp = adapter->hats; - while(tmp && tmp->cookie != (IOHIDElementCookie)cookie) + while (tmp && tmp->cookie != (IOHIDElementCookie)cookie) tmp = tmp->next; - if(tmp->cookie == (IOHIDElementCookie)cookie) + if (tmp->cookie == (IOHIDElementCookie)cookie) { CFIndex range = IOHIDElementGetLogicalMax(element) - IOHIDElementGetLogicalMin(element); CFIndex val = IOHIDValueGetIntegerValue(value); - if(range == 3) + if (range == 3) val *= 2; switch(val) @@ -347,12 +348,12 @@ static void iohidmanager_hid_device_input_callback(void *data, IOReturn result, default: tmp = adapter->axes; - while(tmp && tmp->cookie != (IOHIDElementCookie)cookie) + while (tmp && tmp->cookie != (IOHIDElementCookie)cookie) tmp = tmp->next; if (tmp) { - if(tmp->cookie == (IOHIDElementCookie)cookie) + if (tmp->cookie == (IOHIDElementCookie)cookie) { CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex state = IOHIDValueGetIntegerValue(value) - min; @@ -384,16 +385,17 @@ static void iohidmanager_hid_device_input_callback(void *data, IOReturn result, if (pushed_button) { - tmp = adapter->buttons; - uint8_t bit = 0; - while(tmp && tmp->cookie != (IOHIDElementCookie)cookie) + + tmp = adapter->buttons; + + while (tmp && tmp->cookie != (IOHIDElementCookie)cookie) { bit++; tmp = tmp->next; } - if(tmp && tmp->cookie == (IOHIDElementCookie)cookie) + if (tmp && tmp->cookie == (IOHIDElementCookie)cookie) { CFIndex state = IOHIDValueGetIntegerValue(value); if (state) @@ -425,19 +427,21 @@ static void iohidmanager_hid_device_remove(void *data, if (adapter) { apple_input_rec_t* tmp = NULL; - while(adapter->hats != NULL) + while (adapter->hats != NULL) { tmp = adapter->hats; adapter->hats = adapter->hats->next; free(tmp); } - while(adapter->axes != NULL) + + while (adapter->axes != NULL) { tmp = adapter->axes; adapter->axes = adapter->axes->next; free(tmp); } - while(adapter->buttons != NULL) + + while (adapter->buttons != NULL) { tmp = adapter->buttons; adapter->buttons = adapter->buttons->next; @@ -450,11 +454,11 @@ static void iohidmanager_hid_device_remove(void *data, static int32_t iohidmanager_hid_device_get_int_property( IOHIDDeviceRef device, CFStringRef key) { - int32_t value; CFNumberRef ref = (CFNumberRef)IOHIDDeviceGetProperty(device, key); if (ref && (CFGetTypeID(ref) == CFNumberGetTypeID())) { + int32_t value = 0; CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType, &value); return value; } @@ -581,17 +585,18 @@ static void iohidmanager_hid_device_add(IOHIDDeviceRef device, iohidmanager_hid_ for (i = 0; i < count; i++) { + IOHIDElementType type; + uint32_t page, use, cookie; + int detected_button = 0; IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); if (!element) continue; - IOHIDElementType type = IOHIDElementGetType(element); - uint32_t page = (uint32_t)IOHIDElementGetUsagePage(element); - uint32_t use = (uint32_t)IOHIDElementGetUsage(element); - uint32_t cookie = (uint32_t)IOHIDElementGetCookie(element); - - int detected_button = 0; + type = IOHIDElementGetType(element); + page = (uint32_t)IOHIDElementGetUsagePage(element); + use = (uint32_t)IOHIDElementGetUsage(element); + cookie = (uint32_t)IOHIDElementGetCookie(element); switch (page) { @@ -636,21 +641,21 @@ static void iohidmanager_hid_device_add(IOHIDDeviceRef device, iohidmanager_hid_ axis->cookie = (IOHIDElementCookie)cookie; axis->next = NULL; - if(iohidmanager_check_for_id(adapter->axes,i)) + if (iohidmanager_check_for_id(adapter->axes,i)) { /* axis ID already exists, save to tmp for appending later */ - if(tmpAxes) + if (tmpAxes) iohidmanager_append_record(tmpAxes, axis); else - tmpAxes = axis; + tmpAxes = axis; } else { found_axis[axis->id] = true; - if(adapter->axes) + if (adapter->axes) iohidmanager_append_record(adapter->axes, axis); else - adapter->axes = axis; + adapter->axes = axis; } } else @@ -687,16 +692,16 @@ static void iohidmanager_hid_device_add(IOHIDDeviceRef device, iohidmanager_hid_ btn->cookie = (IOHIDElementCookie)cookie; btn->next = NULL; - if(iohidmanager_check_for_id(adapter->buttons,btn->id)) + if (iohidmanager_check_for_id(adapter->buttons,btn->id)) { - if(tmpButtons) + if (tmpButtons) iohidmanager_append_record_ordered(&tmpButtons, btn); else tmpButtons = btn; } else { - if(adapter->buttons) + if (adapter->buttons) iohidmanager_append_record_ordered(&adapter->buttons, btn); else adapter->buttons = btn; @@ -707,13 +712,13 @@ static void iohidmanager_hid_device_add(IOHIDDeviceRef device, iohidmanager_hid_ /* take care of buttons/axes with duplicate 'use' values */ for (i = 0; i < 6; i++) { - if(found_axis[i] == false && tmpAxes) + if (found_axis[i] == false && tmpAxes) { apple_input_rec_t *next = tmpAxes->next; tmpAxes->id = i; tmpAxes->next = NULL; iohidmanager_append_record(adapter->axes, tmpAxes); - tmpAxes = next; + tmpAxes = next; } } @@ -721,11 +726,11 @@ static void iohidmanager_hid_device_add(IOHIDDeviceRef device, iohidmanager_hid_ if (tmp) { - while(tmp->next) + while (tmp->next) tmp = tmp->next; } - while(tmpButtons) + while (tmpButtons) { apple_input_rec_t *next = tmpButtons->next; @@ -746,34 +751,34 @@ static void iohidmanager_hid_device_add(IOHIDDeviceRef device, iohidmanager_hid_ error: { apple_input_rec_t *tmp = NULL; - while(adapter->hats != NULL) + while (adapter->hats != NULL) { - tmp = adapter->hats; - adapter->hats = adapter->hats->next; + tmp = adapter->hats; + adapter->hats = adapter->hats->next; free(tmp); } - while(adapter->axes != NULL) + while (adapter->axes != NULL) { - tmp = adapter->axes; - adapter->axes = adapter->axes->next; + tmp = adapter->axes; + adapter->axes = adapter->axes->next; free(tmp); } - while(adapter->buttons != NULL) + while (adapter->buttons != NULL) { tmp = adapter->buttons; adapter->buttons = adapter->buttons->next; free(tmp); } - while(tmpAxes != NULL) + while (tmpAxes != NULL) { - tmp = tmpAxes; - tmpAxes = tmpAxes->next; + tmp = tmpAxes; + tmpAxes = tmpAxes->next; free(tmp); } - while(tmpButtons != NULL) + while (tmpButtons != NULL) { - tmp = tmpButtons; - tmpButtons = tmpButtons->next; + tmp = tmpButtons; + tmpButtons = tmpButtons->next; free(tmp); } free(adapter); @@ -833,14 +838,6 @@ static int iohidmanager_hid_manager_free(iohidmanager_hid_t *hid) return 0; } -static int iohidmanager_hid_manager_set_device_matching( - iohidmanager_hid_t *hid) -{ - CFSetRef set = IOHIDManagerCopyDevices(hid->ptr); - CFIndex num_devices = CFSetGetCount(set); - IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); - CFSetGetValues(set, (const void **) device_array); - /* re order device by location id */ typedef struct hid_list { @@ -848,61 +845,73 @@ static int iohidmanager_hid_manager_set_device_matching( uint32_t lid; struct hid_list *next; } hid_list_t; + +static int iohidmanager_hid_manager_set_device_matching( + iohidmanager_hid_t *hid) +{ + unsigned i; + hid_list_t* devList = NULL; + CFSetRef set = IOHIDManagerCopyDevices(hid->ptr); + CFIndex num_devices = CFSetGetCount(set); + IOHIDDeviceRef *device_array = (IOHIDDeviceRef*)calloc(num_devices, sizeof(IOHIDDeviceRef)); + + CFSetGetValues(set, (const void **) device_array); - hid_list_t* devList = NULL; - for (long i=0; idevice = dev; - devList->lid = iohidmanager_hid_device_get_location_id(dev); - //printf("%d\n",devList->lid); - devList->next = NULL; - } - else - { - hid_list_t * new = (hid_list_t *)malloc(sizeof(hid_list_t)); - new->device = dev; - new->lid = iohidmanager_hid_device_get_location_id(dev); - //printf("%d\n",new->lid); - new->next = NULL; + for (i = 0; i < num_devices; i++) + { + IOHIDDeviceRef dev = device_array[i]; - hid_list_t * ptr = devList; - if ( new->lid < ptr->lid ) - { - new->next = ptr; - devList = new; - } - else - { - while ( ( ptr->lid < new->lid ) && (ptr->next != NULL) ) - { - ptr = ptr->next; - } - new->next = ptr->next; - ptr->next = new; - } - } - } - } + /* filter gamepad/joystick devices */ + if ( IOHIDDeviceConformsTo(dev, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) + || IOHIDDeviceConformsTo(dev, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad) + ) + { + if (!devList) + { + devList = (hid_list_t *)malloc(sizeof(hid_list_t)); + devList->device = dev; + devList->lid = iohidmanager_hid_device_get_location_id(dev); + devList->next = NULL; + } + else + { + hid_list_t *ptr = NULL; + hid_list_t *new = (hid_list_t *)malloc(sizeof(hid_list_t)); + new->device = dev; + new->lid = iohidmanager_hid_device_get_location_id(dev); + new->next = NULL; + + ptr = devList; + + if ( new->lid < ptr->lid ) + { + new->next = ptr; + devList = new; + } + else + { + while ((ptr->lid < new->lid ) && ptr->next) + ptr = ptr->next; + + new->next = ptr->next; + ptr->next = new; + } + } + } + } + + { + /* register devices */ + hid_list_t * ptr = devList; + while (ptr) + { + iohidmanager_hid_device_add(ptr->device, hid); + ptr = ptr->next; + free(devList); + devList = ptr; + } + } - /* register devices */ - hid_list_t * ptr = devList; - while (ptr != NULL) - { - iohidmanager_hid_device_add(ptr->device, hid); - //printf("%d\n",ptr->lid); - ptr = ptr->next; - free(devList); - devList = ptr; - } free(device_array); return 0; @@ -914,7 +923,8 @@ static void *iohidmanager_hid_init(void) calloc(1, sizeof(*hid_apple)); if (!hid_apple) - goto error; + return NULL; + hid_apple->slots = pad_connection_init(MAX_USERS); if (!hid_apple->slots) From 8acc085dec594ddfddea78b2f701ffc38605acb7 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 30 Mar 2018 13:57:50 +0200 Subject: [PATCH 089/517] (input_overlay.c) Get rid of some forward declarations --- input/input_overlay.c | 294 ++++++++++++++++++++++-------------------- 1 file changed, 156 insertions(+), 138 deletions(-) diff --git a/input/input_overlay.c b/input/input_overlay.c index 136117e7fe..99d57895bf 100644 --- a/input/input_overlay.c +++ b/input/input_overlay.c @@ -72,8 +72,121 @@ struct input_overlay input_overlay_t *overlay_ptr = NULL; +/** + * input_overlay_add_inputs: + * @ol : pointer to overlay + * @port : the user to show the inputs of + * + * Adds inputs from current_input to the overlay, so it's displayed + * returns true if an input that is pressed will change the overlay + */ +static bool input_overlay_add_inputs_inner(overlay_desc_t *desc, + unsigned port, unsigned analog_dpad_mode) +{ + switch(desc->type) + { + case OVERLAY_TYPE_BUTTONS: + { + unsigned i; + bool all_buttons_pressed = false; + + /*Check each bank of the mask*/ + for (i = 0; i < ARRAY_SIZE(desc->button_mask.data); ++i) + { + /*Get bank*/ + uint32_t bank_mask = BITS_GET_ELEM(desc->button_mask,i); + unsigned id = i * 32; + + /*Worth pursuing? Have we got any bits left in here?*/ + while (bank_mask) + { + /*If this bit is set then we need to query the pad + *The button must be pressed.*/ + if (bank_mask & 1) + { + /* Light up the button if pressed */ + if (input_state(port, RETRO_DEVICE_JOYPAD, 0, id)) + { + all_buttons_pressed = true; + desc->updated = true; + } + else + { + /*we need ALL of the inputs to be active*/ + all_buttons_pressed = false; + desc->updated = false; + + /*abort*/ + return false; + } + } + + bank_mask >>= 1; + ++id; + } + } + + return all_buttons_pressed; + } + + case OVERLAY_TYPE_ANALOG_LEFT: + case OVERLAY_TYPE_ANALOG_RIGHT: + { + unsigned int index = (desc->type == OVERLAY_TYPE_ANALOG_RIGHT) ? + RETRO_DEVICE_INDEX_ANALOG_RIGHT : RETRO_DEVICE_INDEX_ANALOG_LEFT; + + float analog_x = input_state(port, RETRO_DEVICE_ANALOG, + index, RETRO_DEVICE_ID_ANALOG_X); + float analog_y = input_state(port, RETRO_DEVICE_ANALOG, + index, RETRO_DEVICE_ID_ANALOG_Y); + float dx = (analog_x/0x8000)*(desc->range_x/2); + float dy = (analog_y/0x8000)*(desc->range_y/2); + + desc->delta_x = dx; + desc->delta_y = dy; + + /*Maybe use some option here instead of 0, only display + changes greater than some magnitude. + */ + if ((dx * dx) > 0 || (dy*dy) > 0) + return true; + } + break; + + case OVERLAY_TYPE_KEYBOARD: + if (input_state(port, RETRO_DEVICE_KEYBOARD, 0, desc->retro_key_idx)) + { + desc->updated = true; + return true; + } + break; + + default: + break; + } + + return false; +} + static bool input_overlay_add_inputs(input_overlay_t *ol, - unsigned port, unsigned analog_dpad_mode); + unsigned port, unsigned analog_dpad_mode) +{ + unsigned i; + bool button_pressed = false; + input_overlay_state_t *ol_state = &ol->overlay_state; + + if (!ol_state) + return false; + + for (i = 0; i < ol->active->size; i++) + { + overlay_desc_t *desc = &(ol->active->descs[i]); + button_pressed |= input_overlay_add_inputs_inner(desc, + port, analog_dpad_mode); + } + + return button_pressed; +} /** * input_overlay_scale: * @ol : Overlay handle. @@ -234,7 +347,8 @@ static void input_overlay_enable(input_overlay_t *ol, bool enable) * Check whether the given @x and @y coordinates of the overlay * descriptor @desc is inside the overlay descriptor's hitbox. * - * Returns: true (1) if X, Y coordinates are inside a hitbox, otherwise false (0). + * Returns: true (1) if X, Y coordinates are inside a hitbox, + * otherwise false (0). **/ static bool inside_hitbox(const struct overlay_desc *desc, float x, float y) { @@ -305,7 +419,9 @@ static void input_overlay_poll( { case OVERLAY_TYPE_BUTTONS: { - bits_or_bits(out->buttons.data, desc->button_mask.data, ARRAY_SIZE(desc->button_mask.data)); + bits_or_bits(out->buttons.data, + desc->button_mask.data, + ARRAY_SIZE(desc->button_mask.data)); if (BIT256_GET(desc->button_mask, RARCH_OVERLAY_NEXT)) ol->next_index = desc->next_index; @@ -317,15 +433,19 @@ static void input_overlay_poll( break; default: { - float x_val = x_dist / desc->range_x; - float y_val = y_dist / desc->range_y; - float x_val_sat = x_val / desc->analog_saturate_pct; - float y_val_sat = y_val / desc->analog_saturate_pct; + float x_val = x_dist / desc->range_x; + float y_val = y_dist / desc->range_y; + float x_val_sat = x_val / desc->analog_saturate_pct; + float y_val_sat = y_val / desc->analog_saturate_pct; - unsigned int base = (desc->type == OVERLAY_TYPE_ANALOG_RIGHT) ? 2 : 0; + unsigned int base = + (desc->type == OVERLAY_TYPE_ANALOG_RIGHT) + ? 2 : 0; - out->analog[base + 0] = clamp_float(x_val_sat, -1.0f, 1.0f) * 32767.0f; - out->analog[base + 1] = clamp_float(y_val_sat, -1.0f, 1.0f) * 32767.0f; + out->analog[base + 0] = clamp_float(x_val_sat, -1.0f, 1.0f) + * 32767.0f; + out->analog[base + 1] = clamp_float(y_val_sat, -1.0f, 1.0f) + * 32767.0f; } break; } @@ -498,9 +618,12 @@ void input_overlay_loaded(void *task_data, void *user_data, const char *err) } #endif - if (!data->overlay_enable || !video_driver_overlay_interface(&iface) || !iface) + if ( !data->overlay_enable || + !video_driver_overlay_interface(&iface) || + !iface) { - RARCH_ERR("Overlay interface is not present in video driver, or not enabled.\n"); + RARCH_ERR("Overlay interface is not present in video driver," + " or not enabled.\n"); goto abort_load; } @@ -532,14 +655,16 @@ abort_load: free(data); } -void input_overlay_set_visibility(int overlay_idx,enum overlay_visibility vis) +void input_overlay_set_visibility(int overlay_idx, + enum overlay_visibility vis) { - int i; input_overlay_t *ol = overlay_ptr; if (!visibility) { - visibility = (enum overlay_visibility *)calloc(MAX_VISIBILITY,sizeof(enum overlay_visibility)); + unsigned i; + visibility = (enum overlay_visibility *)calloc( + MAX_VISIBILITY, sizeof(enum overlay_visibility)); for (i = 0; i < MAX_VISIBILITY; i++) visibility[i] = OVERLAY_VISIBILITY_DEFAULT; @@ -612,7 +737,8 @@ bool input_overlay_key_pressed(input_overlay_t *ol, unsigned key) * * Poll pressed buttons/keys on currently active overlay. **/ -void input_poll_overlay(input_overlay_t *ol, float opacity, unsigned analog_dpad_mode, +void input_poll_overlay(input_overlay_t *ol, float opacity, + unsigned analog_dpad_mode, float axis_threshold) { rarch_joypad_info_t joypad_info; @@ -661,7 +787,9 @@ void input_poll_overlay(input_overlay_t *ol, float opacity, unsigned analog_dpad else ol->blocked = false; - bits_or_bits(ol_state->buttons.data, polled_data.buttons.data, ARRAY_SIZE(polled_data.buttons.data)); + bits_or_bits(ol_state->buttons.data, + polled_data.buttons.data, + ARRAY_SIZE(polled_data.buttons.data)); for (j = 0; j < ARRAY_SIZE(ol_state->keys); j++) ol_state->keys[j] |= polled_data.keys[j]; @@ -753,7 +881,9 @@ void input_poll_overlay(input_overlay_t *ol, float opacity, unsigned analog_dpad } if (settings->bools.input_overlay_show_physical_inputs) - button_pressed = input_overlay_add_inputs(ol, settings->uints.input_overlay_show_physical_inputs_port, analog_dpad_mode); + button_pressed = input_overlay_add_inputs(ol, + settings->uints.input_overlay_show_physical_inputs_port, + analog_dpad_mode); if (button_pressed || polled) input_overlay_post_poll(ol, opacity); @@ -779,12 +909,14 @@ void input_state_overlay(input_overlay_t *ol, int16_t *ret, case RETRO_DEVICE_KEYBOARD: if (id < RETROK_LAST) { - /*RARCH_LOG("UDLR %u %u %u %u\n", - OVERLAY_GET_KEY(ol_state, RETROK_UP), - OVERLAY_GET_KEY(ol_state, RETROK_DOWN), - OVERLAY_GET_KEY(ol_state, RETROK_LEFT), - OVERLAY_GET_KEY(ol_state, RETROK_RIGHT) - );*/ +#if 0 + RARCH_LOG("UDLR %u %u %u %u\n", + OVERLAY_GET_KEY(ol_state, RETROK_UP), + OVERLAY_GET_KEY(ol_state, RETROK_DOWN), + OVERLAY_GET_KEY(ol_state, RETROK_LEFT), + OVERLAY_GET_KEY(ol_state, RETROK_RIGHT) + ); +#endif if (OVERLAY_GET_KEY(ol_state, id)) *ret |= 1; } @@ -803,117 +935,3 @@ void input_state_overlay(input_overlay_t *ol, int16_t *ret, break; } } -/** - * input_overlay_add_inputs: - * @ol : pointer to overlay - * @port : the user to show the inputs of - * - * Adds inputs from current_input to the overlay, so it's displayed - * returns true if an input that is pressed will change the overlay - */ -static bool input_overlay_add_inputs_inner(overlay_desc_t *desc, - unsigned port, unsigned analog_dpad_mode) -{ - switch(desc->type) - { - case OVERLAY_TYPE_BUTTONS: - { - unsigned i; - unsigned id; - uint32_t bank_mask; - bool all_buttons_pressed = false; - - /*Check each bank of the mask*/ - for (i=0; ibutton_mask.data); ++i) - { - /*Get bank*/ - bank_mask = BITS_GET_ELEM(desc->button_mask,i); - id = i*32; - - /*Worth pursuing? Have we got any bits left in here?*/ - while (bank_mask) - { - /*If this bit is set then we need to query the pad - *The button must be pressed.*/ - if (bank_mask & 1) - { - /* Light up the button if pressed */ - if (input_state(port, RETRO_DEVICE_JOYPAD, 0, id)) - { - all_buttons_pressed = true; - desc->updated = true; - } - else - { - /*we need ALL of the inputs to be active*/ - all_buttons_pressed = false; - desc->updated = false; - - /*abort*/ - return false; - } - } - - bank_mask >>= 1; - ++id; - } - } - - return all_buttons_pressed; - } - - case OVERLAY_TYPE_ANALOG_LEFT: - case OVERLAY_TYPE_ANALOG_RIGHT: - { - unsigned int index = (desc->type == OVERLAY_TYPE_ANALOG_RIGHT) ? - RETRO_DEVICE_INDEX_ANALOG_RIGHT : RETRO_DEVICE_INDEX_ANALOG_LEFT; - - float analog_x = input_state(port, RETRO_DEVICE_ANALOG, index, RETRO_DEVICE_ID_ANALOG_X); - float analog_y = input_state(port, RETRO_DEVICE_ANALOG, index, RETRO_DEVICE_ID_ANALOG_Y); - float dx = (analog_x/0x8000)*(desc->range_x/2); - float dy = (analog_y/0x8000)*(desc->range_y/2); - - desc->delta_x = dx; - desc->delta_y = dy; - - /*Maybe use some option here instead of 0, only display - changes greater than some magnitude. - */ - if ((dx * dx) > 0 || (dy*dy) > 0) - return true; - } - break; - - case OVERLAY_TYPE_KEYBOARD: - if (input_state(port, RETRO_DEVICE_KEYBOARD, 0, desc->retro_key_idx)) - { - desc->updated = true; - return true; - } - break; - - default: - break; - } - - return false; -} - -static bool input_overlay_add_inputs(input_overlay_t *ol, - unsigned port, unsigned analog_dpad_mode) -{ - unsigned i; - bool button_pressed = false; - input_overlay_state_t *ol_state = &ol->overlay_state; - - if (!ol_state) - return false; - - for (i = 0; i < ol->active->size; i++) - { - overlay_desc_t *desc = &(ol->active->descs[i]); - button_pressed |= input_overlay_add_inputs_inner(desc, port, analog_dpad_mode); - } - - return button_pressed; -} From 74d4e6043a5f436d853508884d40c8fd6d05772a Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Fri, 30 Mar 2018 15:44:39 +0200 Subject: [PATCH 090/517] Make MUI auto dpi a bit bigger for low dpi phones --- menu/menu_driver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 7fc7abdcc6..f20fdb3e68 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -551,7 +551,9 @@ float menu_display_get_dpi(void) if (!settings) return true; - dpi = sqrt((width * width) + (height * height)) / 6.5; + /* Generic dpi calculation formula, + * the divider is the screen diagonal in inches */ + dpi = sqrt((width * width) + (height * height)) / 5; if (settings->bools.menu_dpi_override_enable) return settings->uints.menu_dpi_override_value; From 92b46a82750b1996329c07e7c5f714dc600a280b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 30 Mar 2018 15:50:11 +0200 Subject: [PATCH 091/517] Some cleanups --- gfx/common/win32_common.c | 10 +++-- gfx/drivers/d3d10.c | 23 +++++++---- gfx/drivers/d3d11.c | 22 +++++----- gfx/drivers/d3d12.c | 71 +++++++++++++++++++------------- gfx/drivers_context/xegl_ctx.c | 7 ++-- menu/drivers/xmb.c | 74 ++++++++++++++++++++++------------ 6 files changed, 130 insertions(+), 77 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index 6a1af71c1f..b4feaf53e4 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -424,9 +424,10 @@ static int win32_drag_query_file(HWND hwnd, WPARAM wparam) static LRESULT win32_handle_keyboard_event(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { - unsigned keycode; - uint16_t mod = 0; - bool keydown = true; + unsigned keycode = 0; + uint16_t mod = 0; + bool keydown = true; + settings_t *settings = NULL; if (GetKeyState(VK_SHIFT) & 0x80) mod |= RETROKMOD_SHIFT; @@ -460,7 +461,8 @@ static LRESULT win32_handle_keyboard_event(HWND hwnd, UINT message, keydown = false; #if _WIN32_WINNT >= 0x0501 /* XP */ - if (string_is_equal(config_get_ptr()->arrays.input_driver, "raw")) + settings = config_get_ptr(); + if (settings && string_is_equal(settings->arrays.input_driver, "raw")) keycode = input_keymaps_translate_keysym_to_rk((unsigned)(wparam)); else #endif diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index c0f41e9f2d..ca9aab3304 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -499,22 +499,29 @@ static bool d3d10_gfx_read_viewport(void* data, uint8_t* buffer, bool is_idle) static void d3d10_set_menu_texture_frame( void* data, const void* frame, bool rgb32, unsigned width, unsigned height, float alpha) { - d3d10_video_t* d3d10 = (d3d10_video_t*)data; - int pitch = width * (rgb32 ? sizeof(uint32_t) : sizeof(uint16_t)); - DXGI_FORMAT format = rgb32 ? DXGI_FORMAT_B8G8R8A8_UNORM : (DXGI_FORMAT)DXGI_FORMAT_EX_A4R4G4B4_UNORM; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + settings_t* settings = config_get_ptr(); + int pitch = width * (rgb32 ? sizeof(uint32_t) + : sizeof(uint16_t)); + DXGI_FORMAT format = rgb32 ? DXGI_FORMAT_B8G8R8A8_UNORM + : (DXGI_FORMAT)DXGI_FORMAT_EX_A4R4G4B4_UNORM; - if (d3d10->menu.texture.desc.Width != width || d3d10->menu.texture.desc.Height != height) + if ( d3d10->menu.texture.desc.Width != width || + d3d10->menu.texture.desc.Height != height) { - d3d10->menu.texture.desc.Format = d3d10_get_closest_match_texture2D(d3d10->device, format); + d3d10->menu.texture.desc.Format = d3d10_get_closest_match_texture2D( + d3d10->device, format); d3d10->menu.texture.desc.Width = width; d3d10->menu.texture.desc.Height = height; d3d10_init_texture(d3d10->device, &d3d10->menu.texture); } - d3d10_update_texture(width, height, pitch, format, frame, &d3d10->menu.texture); - d3d10->menu.sampler = config_get_ptr()->bools.menu_linear_filter ? d3d10->sampler_linear - : d3d10->sampler_nearest; + d3d10_update_texture(width, height, pitch, format, + frame, &d3d10->menu.texture); + d3d10->menu.sampler = settings->bools.menu_linear_filter + ? d3d10->sampler_linear : d3d10->sampler_nearest; } + static void d3d10_set_menu_texture_enable(void* data, bool state, bool full_screen) { d3d10_video_t* d3d10 = (d3d10_video_t*)data; diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index cddf096d83..7bb3474fec 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -574,9 +574,9 @@ static void* d3d11_gfx_init(const video_info_t* video, const input_driver_t** input, void** input_data) { unsigned i; - WNDCLASSEX wndclass = { 0 }; MONITORINFOEX current_mon; HMONITOR hm_to_use; + WNDCLASSEX wndclass = { 0 }; settings_t* settings = config_get_ptr(); d3d11_video_t* d3d11 = (d3d11_video_t*)calloc(1, sizeof(*d3d11)); @@ -1446,11 +1446,14 @@ static bool d3d11_gfx_read_viewport(void* data, uint8_t* buffer, bool is_idle) static void d3d11_set_menu_texture_frame( void* data, const void* frame, bool rgb32, unsigned width, unsigned height, float alpha) { - d3d11_video_t* d3d11 = (d3d11_video_t*)data; - DXGI_FORMAT format = - rgb32 ? DXGI_FORMAT_B8G8R8A8_UNORM : (DXGI_FORMAT)DXGI_FORMAT_EX_A4R4G4B4_UNORM; + d3d11_video_t* d3d11 = (d3d11_video_t*)data; + settings_t* settings = config_get_ptr(); + DXGI_FORMAT format = rgb32 ? DXGI_FORMAT_B8G8R8A8_UNORM : + (DXGI_FORMAT)DXGI_FORMAT_EX_A4R4G4B4_UNORM; - if (d3d11->menu.texture.desc.Width != width || d3d11->menu.texture.desc.Height != height) + if ( + d3d11->menu.texture.desc.Width != width || + d3d11->menu.texture.desc.Height != height) { d3d11->menu.texture.desc.Format = format; d3d11->menu.texture.desc.Width = width; @@ -1458,11 +1461,12 @@ static void d3d11_set_menu_texture_frame( d3d11_init_texture(d3d11->device, &d3d11->menu.texture); } - d3d11_update_texture(d3d11->context, width, height, 0, format, frame, &d3d11->menu.texture); + d3d11_update_texture(d3d11->context, width, height, 0, + format, frame, &d3d11->menu.texture); d3d11->menu.texture.sampler = d3d11->samplers - [config_get_ptr()->bools.menu_linear_filter - ? RARCH_FILTER_LINEAR - : RARCH_FILTER_NEAREST][RARCH_WRAP_DEFAULT]; + [settings->bools.menu_linear_filter + ? RARCH_FILTER_LINEAR + : RARCH_FILTER_NEAREST][RARCH_WRAP_DEFAULT]; } static void d3d11_set_menu_texture_enable(void* data, bool state, bool full_screen) diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index dafe301cea..16879caa8c 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -871,9 +871,9 @@ static void d3d12_gfx_free(void* data) static void* d3d12_gfx_init(const video_info_t* video, const input_driver_t** input, void** input_data) { - WNDCLASSEX wndclass = { 0 }; MONITORINFOEX current_mon; HMONITOR hm_to_use; + WNDCLASSEX wndclass = { 0 }; settings_t* settings = config_get_ptr(); d3d12_video_t* d3d12 = (d3d12_video_t*)calloc(1, sizeof(*d3d12)); @@ -1597,14 +1597,19 @@ static bool d3d12_gfx_read_viewport(void* data, uint8_t* buffer, bool is_idle) } static void d3d12_set_menu_texture_frame( - void* data, const void* frame, bool rgb32, unsigned width, unsigned height, float alpha) + void* data, const void* frame, bool rgb32, + unsigned width, unsigned height, float alpha) { - d3d12_video_t* d3d12 = (d3d12_video_t*)data; - int pitch = width * (rgb32 ? sizeof(uint32_t) : sizeof(uint16_t)); - DXGI_FORMAT format = - rgb32 ? DXGI_FORMAT_B8G8R8A8_UNORM : (DXGI_FORMAT)DXGI_FORMAT_EX_A4R4G4B4_UNORM; + d3d12_video_t* d3d12 = (d3d12_video_t*)data; + settings_t* settings = config_get_ptr(); + int pitch = width * + (rgb32 ? sizeof(uint32_t) : sizeof(uint16_t)); + DXGI_FORMAT format = rgb32 ? DXGI_FORMAT_B8G8R8A8_UNORM + : (DXGI_FORMAT)DXGI_FORMAT_EX_A4R4G4B4_UNORM; - if (d3d12->menu.texture.desc.Width != width || d3d12->menu.texture.desc.Height != height) + if ( + d3d12->menu.texture.desc.Width != width || + d3d12->menu.texture.desc.Height != height) { d3d12->menu.texture.desc.Width = width; d3d12->menu.texture.desc.Height = height; @@ -1613,13 +1618,14 @@ static void d3d12_set_menu_texture_frame( d3d12_init_texture(d3d12->device, &d3d12->menu.texture); } - d3d12_update_texture(width, height, pitch, format, frame, &d3d12->menu.texture); + d3d12_update_texture(width, height, pitch, + format, frame, &d3d12->menu.texture); d3d12->menu.alpha = alpha; { D3D12_RANGE read_range = { 0, 0 }; - d3d12_vertex_t* v; + d3d12_vertex_t* v = NULL; D3D12Map(d3d12->menu.vbo, 0, &read_range, (void**)&v); v[0].color[3] = alpha; @@ -1629,19 +1635,27 @@ static void d3d12_set_menu_texture_frame( D3D12Unmap(d3d12->menu.vbo, 0, NULL); } - d3d12->menu.texture.sampler = config_get_ptr()->bools.menu_linear_filter - ? d3d12->samplers[RARCH_FILTER_LINEAR][RARCH_WRAP_DEFAULT] - : d3d12->samplers[RARCH_FILTER_NEAREST][RARCH_WRAP_DEFAULT]; + d3d12->menu.texture.sampler = settings->bools.menu_linear_filter + ? d3d12->samplers[RARCH_FILTER_LINEAR][RARCH_WRAP_DEFAULT] + : d3d12->samplers[RARCH_FILTER_NEAREST][RARCH_WRAP_DEFAULT]; } -static void d3d12_set_menu_texture_enable(void* data, bool state, bool full_screen) + +static void d3d12_set_menu_texture_enable(void* data, + bool state, bool full_screen) { - d3d12_video_t* d3d12 = (d3d12_video_t*)data; + d3d12_video_t* d3d12 = (d3d12_video_t*)data; + + if (!d3d12) + return; d3d12->menu.enabled = state; d3d12->menu.fullscreen = full_screen; } -static void d3d12_gfx_show_mouse(void* data, bool state) { win32_show_cursor(state); } +static void d3d12_gfx_show_mouse(void* data, bool state) +{ + win32_show_cursor(state); +} static void d3d12_gfx_set_aspect_ratio(void* data, unsigned aspect_ratio_idx) { @@ -1663,21 +1677,21 @@ static void d3d12_gfx_apply_state_changes(void* data) } static void d3d12_gfx_set_osd_msg( - void* data, video_frame_info_t* video_info, const char* msg, const void* params, void* font) + void* data, video_frame_info_t* video_info, + const char* msg, const void* params, void* font) { d3d12_video_t* d3d12 = (d3d12_video_t*)data; - if (d3d12) - { - if (d3d12->sprites.enabled) - font_driver_render_msg(video_info, font, msg, (const struct font_params*)params); - else - printf("OSD msg: %s\n", msg); - } + if (!d3d12 || !d3d12->sprites.enabled) + return; + + font_driver_render_msg(video_info, font, msg, + (const struct font_params*)params); } static uintptr_t d3d12_gfx_load_texture( - void* video_data, void* data, bool threaded, enum texture_filter_type filter_type) + void* video_data, void* data, bool threaded, + enum texture_filter_type filter_type) { d3d12_texture_t* texture = NULL; d3d12_video_t* d3d12 = (d3d12_video_t*)video_data; @@ -1696,12 +1710,14 @@ static uintptr_t d3d12_gfx_load_texture( case TEXTURE_FILTER_MIPMAP_LINEAR: texture->desc.MipLevels = UINT16_MAX; case TEXTURE_FILTER_LINEAR: - texture->sampler = d3d12->samplers[RARCH_FILTER_LINEAR][RARCH_WRAP_EDGE]; + texture->sampler = d3d12->samplers[ + RARCH_FILTER_LINEAR][RARCH_WRAP_EDGE]; break; case TEXTURE_FILTER_MIPMAP_NEAREST: texture->desc.MipLevels = UINT16_MAX; case TEXTURE_FILTER_NEAREST: - texture->sampler = d3d12->samplers[RARCH_FILTER_NEAREST][RARCH_WRAP_EDGE]; + texture->sampler = d3d12->samplers[ + RARCH_FILTER_NEAREST][RARCH_WRAP_EDGE]; break; } @@ -1713,7 +1729,8 @@ static uintptr_t d3d12_gfx_load_texture( d3d12_init_texture(d3d12->device, texture); d3d12_update_texture( - image->width, image->height, 0, DXGI_FORMAT_B8G8R8A8_UNORM, image->pixels, texture); + image->width, image->height, 0, + DXGI_FORMAT_B8G8R8A8_UNORM, image->pixels, texture); return (uintptr_t)texture; } diff --git a/gfx/drivers_context/xegl_ctx.c b/gfx/drivers_context/xegl_ctx.c index e791bf1f42..b470ccc6db 100644 --- a/gfx/drivers_context/xegl_ctx.c +++ b/gfx/drivers_context/xegl_ctx.c @@ -273,6 +273,7 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, XSetWindowAttributes swa = {0}; XVisualInfo *vi = NULL; xegl_ctx_data_t *xegl = (xegl_ctx_data_t*)data; + settings_t *settings = config_get_ptr(); int (*old_handler)(Display*, XErrorEvent*) = NULL; @@ -344,10 +345,10 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, CWOverrideRedirect, &swa); XSetWindowBackground(g_x11_dpy, g_x11_win, 0); - if (fullscreen && config_get_ptr()->bools.video_disable_composition) + if (fullscreen && settings && settings->bools.video_disable_composition) { - uint32_t value = 1; - Atom cardinal = XInternAtom(g_x11_dpy, "CARDINAL", False); + uint32_t value = 1; + Atom cardinal = XInternAtom(g_x11_dpy, "CARDINAL", False); Atom net_wm_bypass_compositor = XInternAtom(g_x11_dpy, "_NET_WM_BYPASS_COMPOSITOR", False); RARCH_LOG("[X/EGL]: Requesting compositor bypass.\n"); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 83e0a598e3..cd4cd47d7d 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -513,7 +513,7 @@ static xmb_node_t *xmb_copy_node(const xmb_node_t *old_node) static const char *xmb_thumbnails_ident(char pos) { - char folder; + char folder = 0; settings_t *settings = config_get_ptr(); if (pos == 'R') @@ -598,7 +598,8 @@ static size_t xmb_list_get_size(void *data, enum menu_list_type type) return 0; } -static void *xmb_list_get_entry(void *data, enum menu_list_type type, unsigned i) +static void *xmb_list_get_entry(void *data, + enum menu_list_type type, unsigned i) { size_t list_size = 0; xmb_handle_t *xmb = (xmb_handle_t*)data; @@ -1571,7 +1572,8 @@ static void xmb_list_open_new(xmb_handle_t *xmb, xmb->old_depth = xmb->depth; menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &skip); - xmb_system_tab = xmb_get_system_tab(xmb, (unsigned)xmb->categories_selection_ptr); + xmb_system_tab = xmb_get_system_tab(xmb, + (unsigned)xmb->categories_selection_ptr); if (xmb_system_tab <= XMB_SYSTEM_TAB_SETTINGS) { @@ -1584,10 +1586,11 @@ static void xmb_list_open_new(xmb_handle_t *xmb, } } -static xmb_node_t *xmb_node_allocate_userdata(xmb_handle_t *xmb, unsigned i) +static xmb_node_t *xmb_node_allocate_userdata( + xmb_handle_t *xmb, unsigned i) { + xmb_node_t *tmp = NULL; xmb_node_t *node = xmb_alloc_node(); - xmb_node_t *tmp; if (!node) { @@ -1604,7 +1607,8 @@ static xmb_node_t *xmb_node_allocate_userdata(xmb_handle_t *xmb, unsigned i) node->zoom = xmb->categories_active_zoom; } - tmp = (xmb_node_t*)file_list_get_userdata_at_offset(xmb->horizontal_list, i); + tmp = (xmb_node_t*)file_list_get_userdata_at_offset( + xmb->horizontal_list, i); xmb_free_node(tmp); file_list_set_userdata(xmb->horizontal_list, i, node); @@ -1619,7 +1623,8 @@ static xmb_node_t* xmb_get_userdata_from_horizontal_list( file_list_get_userdata_at_offset(xmb->horizontal_list, i); } -static void xmb_push_animations(xmb_node_t *node, uintptr_t tag, float ia, float ix) +static void xmb_push_animations(xmb_node_t *node, + uintptr_t tag, float ia, float ix) { menu_animation_ctx_entry_t anim_entry; @@ -1654,7 +1659,8 @@ static void xmb_list_switch_old(xmb_handle_t *xmb, last = end > 0 ? end - 1 : 0; video_driver_get_size(NULL, &height); - xmb_calculate_visible_range(xmb, height, end, current, &first, &last); + xmb_calculate_visible_range(xmb, height, end, + current, &first, &last); for (i = 0; i < end; i++) { @@ -1773,7 +1779,8 @@ static void xmb_set_title(xmb_handle_t *xmb) if (!path) return; - fill_pathname_base_noext(xmb->title_name, path, sizeof(xmb->title_name)); + fill_pathname_base_noext( + xmb->title_name, path, sizeof(xmb->title_name)); } } @@ -1867,7 +1874,8 @@ static void xmb_list_switch(xmb_handle_t *xmb) xmb_list_switch_horizontal_list(xmb); anim_entry.duration = XMB_DELAY; - anim_entry.target_value = xmb->icon_spacing_horizontal * -(float)xmb->categories_selection_ptr; + anim_entry.target_value = xmb->icon_spacing_horizontal + * -(float)xmb->categories_selection_ptr; anim_entry.subject = &xmb->categories_x_pos; anim_entry.easing_enum = EASING_OUT_QUAD; /* TODO/FIXME - integer conversion resulted in change of sign */ @@ -2045,11 +2053,12 @@ static void xmb_context_reset_horizontal_list( size_t list_size = xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL); - xmb->categories_x_pos = xmb->icon_spacing_horizontal * + xmb->categories_x_pos = + xmb->icon_spacing_horizontal * -(float)xmb->categories_selection_ptr; - depth = (xmb->depth > 1) ? 2 : 1; - xmb->x = xmb->icon_size * -(depth*2-2); + depth = (xmb->depth > 1) ? 2 : 1; + xmb->x = xmb->icon_size * -(depth*2-2); for (i = 0; i < list_size; i++) { @@ -2077,9 +2086,12 @@ static void xmb_context_reset_horizontal_list( { struct texture_image ti; char sysname[256]; - char *iconpath = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); - char *texturepath = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); - char *content_texturepath = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); + char *iconpath = (char*) + malloc(PATH_MAX_LENGTH * sizeof(char)); + char *texturepath = (char*) + malloc(PATH_MAX_LENGTH * sizeof(char)); + char *content_texturepath = (char*) + malloc(PATH_MAX_LENGTH * sizeof(char)); iconpath[0] = sysname[0] = texturepath[0] = content_texturepath[0] = '\0'; @@ -2149,7 +2161,8 @@ static void xmb_refresh_horizontal_list(xmb_handle_t *xmb) menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); - xmb->horizontal_list = (file_list_t*)calloc(1, sizeof(file_list_t)); + xmb->horizontal_list = (file_list_t*) + calloc(1, sizeof(file_list_t)); if (xmb->horizontal_list) xmb_init_horizontal_list(xmb); @@ -2338,7 +2351,8 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, if (core_node) return core_node->content_icon; - switch (xmb_get_system_tab(xmb, (unsigned)xmb->categories_selection_ptr)) + switch (xmb_get_system_tab(xmb, + (unsigned)xmb->categories_selection_ptr)) { case XMB_SYSTEM_TAB_FAVORITES: return xmb->textures.list[XMB_TEXTURE_FAVORITE]; @@ -2413,11 +2427,13 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, #ifdef HAVE_NETWORKING case MENU_ROOM: return xmb->textures.list[XMB_TEXTURE_ROOM]; - /* stub these out until we have the icons +#if 0 + /* stub these out until we have the icons */ case MENU_ROOM_LAN: return xmb->textures.list[XMB_TEXTURE_ROOM_LAN]; case MENU_ROOM_MITM: - return xmb->textures.list[XMB_TEXTURE_ROOM_MITM]; */ + return xmb->textures.list[XMB_TEXTURE_ROOM_MITM]; +#endif #endif } @@ -2452,7 +2468,8 @@ static void xmb_calculate_visible_range(const xmb_handle_t *xmb, { for (j = current; j-- > 0; ) { - float bottom = xmb_item_y(xmb, j, current) + base_y + xmb->icon_size; + float bottom = xmb_item_y(xmb, j, current) + + base_y + xmb->icon_size; if (bottom < 0) break; @@ -2538,16 +2555,20 @@ static int xmb_draw_item( } } - if (string_is_equal(entry->value, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) || - (string_is_equal(entry->value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))) + if (string_is_equal(entry->value, + msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) || + (string_is_equal(entry->value, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))) { if (xmb->textures.list[XMB_TEXTURE_SWITCH_OFF]) texture_switch = xmb->textures.list[XMB_TEXTURE_SWITCH_OFF]; else do_draw_text = true; } - else if (string_is_equal(entry->value, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) || - (string_is_equal(entry->value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)))) + else if (string_is_equal(entry->value, + msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) || + (string_is_equal(entry->value, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)))) { if (xmb->textures.list[XMB_TEXTURE_SWITCH_ON]) texture_switch = xmb->textures.list[XMB_TEXTURE_SWITCH_ON]; @@ -2834,7 +2855,8 @@ static void xmb_render(void *data, bool is_idle) video_driver_get_size(NULL, &height); if (height) - xmb_calculate_visible_range(xmb, height, end, selection, &first, &last); + xmb_calculate_visible_range(xmb, height, + end, selection, &first, &last); for (i = first; i <= last; i++) { From b24433a9f24630f18a4304f4f595b6d9600f5592 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 30 Mar 2018 16:05:45 +0200 Subject: [PATCH 092/517] Hack - make diagonal 5.0f for mobile, 6.5f for desktop --- menu/menu_driver.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/menu/menu_driver.c b/menu/menu_driver.c index f20fdb3e68..dde5ee212b 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -542,18 +542,23 @@ void menu_display_unset_framebuffer_dirty_flag(void) * RGUI or XMB use this. */ float menu_display_get_dpi(void) { - settings_t *settings = config_get_ptr(); - float dpi; unsigned width, height; + settings_t *settings = config_get_ptr(); + float dpi = 0.0f; + float diagonal = 6.5f; video_driver_get_size(&width, &height); if (!settings) return true; +#ifdef RARCH_MOBILE + diagonal = 5.0f; +#endif + /* Generic dpi calculation formula, * the divider is the screen diagonal in inches */ - dpi = sqrt((width * width) + (height * height)) / 5; + dpi = sqrt((width * width) + (height * height)) / diagonal; if (settings->bools.menu_dpi_override_enable) return settings->uints.menu_dpi_override_value; From cf4ab13281d1d84d21fd49b53d375793697e9dda Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 30 Mar 2018 19:28:37 +0200 Subject: [PATCH 093/517] Revert "Silence some Clang static analyzer warnings" This reverts commit 2c882a01c12cd3104964c70f95b4037fdd6dc4e5. --- libretro-common/net/net_http.c | 1 + menu/menu_displaylist.c | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libretro-common/net/net_http.c b/libretro-common/net/net_http.c index caa6e56c93..3b25f340d9 100644 --- a/libretro-common/net/net_http.c +++ b/libretro-common/net/net_http.c @@ -138,6 +138,7 @@ void net_http_urlencode_full(char *dest, char *tmp = NULL; char url_domain[PATH_MAX_LENGTH] = {0}; char url_path[PATH_MAX_LENGTH] = {0}; + char url_encoded[PATH_MAX_LENGTH] = {0}; int count = 0; strlcpy (url_path, source, sizeof(url_path)); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 0fab70e26c..f7d85028dd 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3282,12 +3282,15 @@ static int menu_displaylist_parse_options_remappings( { for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) { + unsigned user = settings->uints.keymapper_port + 1; + unsigned desc_offset = retro_id; char descriptor[255]; - const struct retro_keybind *keybind = &input_config_binds[settings->uints.keymapper_port][retro_id]; - const struct retro_keybind *auto_bind = (const struct retro_keybind*) - input_config_get_bind_auto(settings->uints.keymapper_port, retro_id); + const struct retro_keybind *auto_bind = NULL; + const struct retro_keybind *keybind = NULL; - descriptor[0] = '\0'; + keybind = &input_config_binds[settings->uints.keymapper_port][retro_id]; + auto_bind = (const struct retro_keybind*) + input_config_get_bind_auto(settings->uints.keymapper_port, retro_id); input_config_get_bind_string(descriptor, keybind, auto_bind, sizeof(descriptor)); From 0c0cad86303368760a9abe6f53a23de9b1bd6fc3 Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Fri, 30 Mar 2018 23:53:16 +0200 Subject: [PATCH 094/517] XMB Right Thumbnail rework. --- menu/drivers/xmb.c | 134 ++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 70 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index cd4cd47d7d..2418d79e0b 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -279,27 +279,8 @@ typedef struct xmb_handle video_font_raster_block_t raster_block2; } xmb_handle_t; -/* scaling multiplier resulting from equations using values below */ -/* applied in xmb_init */ -/* values for xmb_scale 50 = {2.5, 2.5, 2, 1.7, 2.5, 4, 2.4, 2.5} */ -/* values for xmb_scale 75 = {2, 1.6, 1.6, 1.4, 1.5, 2.3, 1.9, 1.3} */ float scale_mod[8] = { - /* text length & word wrap (base 35 apply to file browser, 1st column) */ - 1, - /* playlist text length when thumbnail is ON (small, base 40) */ - 1, - /* playlist text length when thumbnail is OFF (large, base 70) */ - 1, - /* sub-label length & word wrap */ - 1, - /* thumbnail size & vertical margin from top */ - 1, - /* thumbnail horizontal left margin (horizontal positioning) */ - 1, - /* margin before 2nd column start (shaders parameters, cheats...) */ - 1, - /* text length & word wrap (base 35 apply to 2nd column in cheats, shaders...) */ - 1 + 1, 1, 1, 1, 1, 1, 1, 1 }; static float coord_shadow[] = { @@ -3048,7 +3029,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) size_t percent_width = 0; math_matrix_4x4 mymat; unsigned i; - float thumb_width, thumb_height, left_thumb_width, left_thumb_height; + float thumb_width, thumb_height, left_thumb_width, left_thumb_height, thumb_max_width; menu_display_ctx_rotate_draw_t rotate_draw; char msg[1024]; char title_msg[255]; @@ -3062,7 +3043,9 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb_handle_t *xmb = (xmb_handle_t*)data; float window_width = video_info->width; float window_height = video_info->height; - const float around_thumb_margin = 0.96; + const float under_thumb_margin = 0.96; + float scale_factor = (settings->uints.menu_xmb_scale_factor * window_width) / (1920.0 * 100); + float pseudo_font_length = xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4; if (!xmb) return; @@ -3139,16 +3122,15 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) if (((xmb->margins_screen_top + xmb->icon_size + min_thumb_size) <= height) && ((xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + - xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4 + min_thumb_size) <= width)) + pseudo_font_length + min_thumb_size) <= width)) { if (xmb->savestate_thumbnail) xmb_draw_thumbnail(video_info, xmb, &coord_white[0], width, height, xmb->margins_screen_left * scale_mod[5] - + xmb->icon_spacing_horizontal + - xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4, + + xmb->icon_spacing_horizontal + pseudo_font_length, xmb->margins_screen_top + xmb->icon_size + xmb->savestate_thumbnail_height * scale_mod[4], - xmb->savestate_thumbnail_width, xmb->savestate_thumbnail_height * scale_mod[4], + xmb->savestate_thumbnail_width * scale_mod[4], xmb->savestate_thumbnail_height * scale_mod[4], xmb->savestate_thumbnail); else if (xmb->thumbnail && !string_is_equal(xmb_thumbnails_ident('R'), @@ -3159,49 +3141,51 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); #endif - /* Limit thumbnail height to screen height + margin. */ - - thumb_width = xmb->thumbnail_width; - thumb_height = xmb->thumbnail_height; - - if (xmb->margins_screen_top + xmb->icon_size + xmb->thumbnail_height * scale_mod[4] >= - (window_height * around_thumb_margin)) - { - thumb_width = thumb_width * - (((window_height * around_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / - (thumb_height * scale_mod[4])); - thumb_height = thumb_height * - (((window_height * around_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / - (thumb_height * scale_mod[4])); - } - /* Limit thumbnail width */ - if (xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + - xmb->icon_spacing_horizontal*4 - xmb->icon_size / 4 + thumb_width * scale_mod[4] >= - (window_width * around_thumb_margin)) + thumb_max_width = window_width - (xmb->icon_size / 6) - (xmb->margins_screen_left * scale_mod[5]) - + xmb->icon_spacing_horizontal - pseudo_font_length; + + if (xmb->thumbnail_width * scale_mod[4] > thumb_max_width) { + thumb_width = (xmb->thumbnail_width * scale_mod[4]) * + (thumb_max_width / (xmb->thumbnail_width * scale_mod[4])); + thumb_height = (xmb->thumbnail_height * scale_mod[4]) * + (thumb_max_width / (xmb->thumbnail_width * scale_mod[4])); + } + else + { + thumb_width = xmb->thumbnail_width * scale_mod[4]; + thumb_height = xmb->thumbnail_height * scale_mod[4]; + } + + /* Limit thumbnail height to screen height + margin. */ + + if (xmb->margins_screen_top + xmb->icon_size + thumb_height >= + (window_height * under_thumb_margin)) + { + thumb_width = thumb_width * + (((window_height * under_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / + thumb_height); thumb_height = thumb_height * - (((window_width * around_thumb_margin) - xmb->margins_screen_left * scale_mod[5] - xmb->icon_spacing_horizontal - - xmb->icon_spacing_horizontal * 4 + xmb->icon_size / 4) / (thumb_width * scale_mod[4])); - thumb_width = thumb_width * - (((window_width * around_thumb_margin) - xmb->margins_screen_left * scale_mod[5] - xmb->icon_spacing_horizontal - - xmb->icon_spacing_horizontal * 4 + xmb->icon_size / 4) / (thumb_width * scale_mod[4])); + (((window_height * under_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / + thumb_height); } xmb_draw_thumbnail(video_info, xmb, &coord_white[0], width, height, - xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + - xmb->icon_spacing_horizontal*4 - xmb->icon_size / 4, - xmb->margins_screen_top + xmb->icon_size + thumb_height * scale_mod[4], - thumb_width * scale_mod[4], thumb_height * scale_mod[4], + window_width - (xmb->icon_size / 6) - thumb_max_width + + ((thumb_max_width - thumb_width) / 2), + xmb->margins_screen_top + xmb->icon_size + thumb_height, + thumb_width, thumb_height, xmb->thumbnail); } } /* Do not draw the left thumbnail if there is no space available */ - if ((xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) <= window_height) + if ((xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) + <= window_height) { /* Left Thumbnail */ @@ -3209,19 +3193,17 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) && !string_is_equal(xmb_thumbnails_ident('L'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { - float scale_factor = (settings->uints.menu_xmb_scale_factor * width) / (1920.0 * 100); - /* Limit left thumbnail height to screen height + margin. */ if (xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + - xmb->left_thumbnail_height >= (float)(height - (96.0 * scale_factor))) + xmb->left_thumbnail_height >= (window_height - (96.0 * scale_factor))) { left_thumb_width = xmb->left_thumbnail_width * - (((float)(height - (96.0 * scale_factor)) - xmb->margins_screen_top - + (((window_height - (96.0 * scale_factor)) - xmb->margins_screen_top - (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / xmb->left_thumbnail_height); left_thumb_height = xmb->left_thumbnail_height * - (((float)(height - (96.0 * scale_factor)) - xmb->margins_screen_top - + (((window_height - (96.0 * scale_factor)) - xmb->margins_screen_top - (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / xmb->left_thumbnail_height); } @@ -3233,8 +3215,9 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb_draw_thumbnail(video_info, xmb, &coord_white[0], width, height, - 20 * scale_factor + ((xmb->left_thumbnail_width - left_thumb_width) / 2), - xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + left_thumb_height, + (xmb->icon_size / 6) + ((xmb->left_thumbnail_width - left_thumb_width) / 2), + xmb->margins_screen_top + xmb->icon_size * + (!(xmb->depth == 1)? 2.1 : 1) + left_thumb_height, left_thumb_width, left_thumb_height, xmb->left_thumbnail); } @@ -3519,7 +3502,7 @@ static void xmb_layout_ps3(xmb_handle_t *xmb, int width) new_header_height = 128.0 * scale_factor; - xmb->thumbnail_width = 460.0 * scale_factor; + xmb->thumbnail_width = 1024.0 * scale_factor; xmb->left_thumbnail_width = 430.0 * scale_factor; xmb->savestate_thumbnail_width= 460.0 * scale_factor; xmb->cursor_size = 64.0 * scale_factor; @@ -3753,16 +3736,27 @@ static void *xmb_init(void **userdata, bool video_is_threaded) menu_handle_t *menu = (menu_handle_t*)calloc(1, sizeof(*menu)); float scale_value = settings->uints.menu_xmb_scale_factor; + /* scaling multiplier formulas made from these values: */ + /* xmb_scale 50 = {2.5, 2.5, 2, 1.7, 2.5, 4, 2.4, 2.5} */ + /* xmb_scale 75 = { 2, 1.6, 1.6, 1.4, 1.5, 2.3, 1.9, 1.3} */ if (scale_value < 100) { - scale_mod[0] = -0.03 * scale_value + 4.083333; + /* text length & word wrap (base 35 apply to file browser, 1st column) */ + scale_mod[0] = -0.03 * scale_value + 4.083; + /* playlist text length when thumbnail is ON (small, base 40) */ scale_mod[1] = -0.03 * scale_value + 3.95; - scale_mod[2] = -0.02 * scale_value + 3.033333; - scale_mod[3] = -0.014 * scale_value + 2.416667; - scale_mod[4] = -0.03 * scale_value + 3.916667; - scale_mod[5] = -0.06 * scale_value + 6.933333; - scale_mod[6] = -0.028 * scale_value + 3.866667; - scale_mod[7] = 134.179 * pow(scale_value, -1.077852); + /* playlist text length when thumbnail is OFF (large, base 70) */ + scale_mod[2] = -0.02 * scale_value + 3.033; + /* sub-label length & word wrap */ + scale_mod[3] = -0.014 * scale_value + 2.416; + /* thumbnail size & vertical margin from top */ + scale_mod[4] = -0.03 * scale_value + 3.916; + /* thumbnail horizontal left margin (horizontal positioning) */ + scale_mod[5] = -0.06 * scale_value + 6.933; + /* margin before 2nd column start (shaders parameters, cheats...) */ + scale_mod[6] = -0.028 * scale_value + 3.866; + /* text length & word wrap (base 35 apply to 2nd column in cheats, shaders, etc) */ + scale_mod[7] = 134.179 * pow(scale_value, -1.0778); for (i = 0; i < 8; i++) if (scale_mod[i] < 1) From ec6d6e58d62125c84cd27de3a15d7da578c6897e Mon Sep 17 00:00:00 2001 From: Dwedit Date: Fri, 30 Mar 2018 20:22:35 -0500 Subject: [PATCH 095/517] Fast Savestate and Hard Audio Disable flags --- dynamic.c | 11 ++++++ libretro-common/include/libretro.h | 43 ++++++++++++++++++---- runahead/run_ahead.c | 58 +++++++++++++++++++++++++++--- runahead/run_ahead.h | 3 ++ 4 files changed, 105 insertions(+), 10 deletions(-) diff --git a/dynamic.c b/dynamic.c index e312e03cc6..972b85b97b 100644 --- a/dynamic.c +++ b/dynamic.c @@ -68,6 +68,7 @@ #ifdef HAVE_RUNAHEAD #include "runahead/secondary_core.h" +#include "runahead/run_ahead.h" #endif #ifdef HAVE_DYNAMIC @@ -1763,6 +1764,16 @@ bool rarch_environment_cb(unsigned cmd, void *data) { result |= 1; } +#ifdef HAVE_RUNAHEAD + if (want_fast_savestate()) + { + result |= 4; + } + if (get_hard_disable_audio()) + { + result |= 8; + } +#endif if (data != NULL) { int* result_p = (int*)data; diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index bfe832c202..f866b0bc77 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1119,12 +1119,43 @@ struct retro_led_interface #define RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE (47 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* int * -- - * Queries the frontend if audio and video are enabled or not. - * If not enabled, the frontend will discard the audio or video, - * so the core may decide to skip producing audio or video. - * Bit 0 (value 1) is set if Video is enabled, - * Bit 1 (value 2) is set if Audio is enabled. - * Other bits are reserved for future use. + * Tells the core if the frontend wants audio or video. + * If disabled, the frontend will discard the audio or video, + * so the core may decide to skip generating a frame or generating audio. + * This is mainly used for increasing performance. + * Bit 0 (value 1): Enable Video + * Bit 1 (value 2): Enable Audio + * Bit 2 (value 4): Use Fast Savestates. + * Bit 3 (value 8): Hard Disable Audio + * Other bits are reserved for future use and will default to zero. + * If video is disabled: + * * The frontend wants the core to not generate any video, + * including presenting frames via hardware acceleration. + * * The frontend's video frame callback will do nothing. + * * After running the frame, the video output of the next frame should be + * no different than if video was enabled, and saving and loading state + * should have no issues. + * If audio is disabled: + * * The frontend wants the core to not generate any audio. + * * The frontend's audio callbacks will do nothing. + * * After running the frame, the audio output of the next frame should be + * no different than if audio was enabled, and saving and loading state + * should have no issues. + * Fast Savestates: + * * Guaranteed to be created by the same binary that will load them. + * * Will not be written to or read from the disk. + * * Suggest that the core updates its memory buffers in-place if possible. + * * Suggest that the core skips clearing memory. + * * Suggest that the core skips resetting the system. + * * Suggest that the core may skip validation steps. + * Hard Disable Audio: + * * Used for a secondary core when running ahead. + * * Indicates that the frontend will never need audio from the core. + * * Suggests that the core may stop synthesizing audio, but this should not + * compromise emulation accuracy. + * * Audio output for the next frame does not matter, and the frontend will + * never need an accurate audio state in the future. + * * State will never be saved when using Hard Disable Audio. */ #define RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE (41 | RETRO_ENVIRONMENT_EXPERIMENTAL) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 9a72ec97af..74551b8a1b 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -23,6 +23,10 @@ static void runahead_suspend_audio(void); static void runahead_resume_audio(void); static void runahead_suspend_video(void); static void runahead_resume_video(void); +static void set_fast_savestate(void); +static void unset_fast_savestate(void); +static void set_hard_disable_audio(void); +static void unset_hard_disable_audio(void); static size_t runahead_save_state_size = -1; @@ -274,7 +278,9 @@ void run_ahead(int runAheadCount, bool useSecondary) { runahead_suspend_video(); runahead_suspend_audio(); + set_hard_disable_audio(); okay = runahead_run_secondary(); + unset_hard_disable_audio(); runahead_resume_audio(); runahead_resume_video(); @@ -285,7 +291,9 @@ void run_ahead(int runAheadCount, bool useSecondary) } } runahead_suspend_audio(); + set_hard_disable_audio(); okay = runahead_run_secondary(); + unset_hard_disable_audio(); runahead_resume_audio(); /* Could not create a secondary core. RunAhead @@ -330,8 +338,10 @@ static bool runahead_save_state(void) { retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; - bool okay = core_serialize(serialize_info); - + bool okay; + set_fast_savestate(); + okay = core_serialize(serialize_info); + unset_fast_savestate(); if (!okay) runahead_error(); return okay; @@ -342,7 +352,10 @@ static bool runahead_load_state(void) retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*) runahead_save_state_list->data[0]; bool lastDirty = input_is_dirty; - bool okay = core_unserialize(serialize_info); + bool okay; + set_fast_savestate(); + okay = core_unserialize(serialize_info); + unset_fast_savestate(); input_is_dirty = lastDirty; if (!okay) @@ -355,7 +368,10 @@ static bool runahead_load_state_secondary(void) { retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; - bool okay = secondary_core_deserialize(serialize_info->data_const, serialize_info->size); + bool okay; + set_fast_savestate(); + okay = secondary_core_deserialize(serialize_info->data_const, serialize_info->size); + unset_fast_savestate(); if (!okay) runahead_secondary_core_available = false; @@ -400,3 +416,37 @@ void runahead_destroy(void) remove_hooks(); runahead_clear_variables(); } + +static bool request_fast_savestate; +static bool hard_disable_audio; + + +bool want_fast_savestate(void) +{ + return request_fast_savestate; +} + +static void set_fast_savestate(void) +{ + request_fast_savestate = true; +} + +static void unset_fast_savestate(void) +{ + request_fast_savestate = false; +} + +bool get_hard_disable_audio(void) +{ + return hard_disable_audio; +} + +static void set_hard_disable_audio(void) +{ + hard_disable_audio = true; +} + +static void unset_hard_disable_audio(void) +{ + hard_disable_audio = false; +} diff --git a/runahead/run_ahead.h b/runahead/run_ahead.h index 967d21b5e8..f1508dd657 100644 --- a/runahead/run_ahead.h +++ b/runahead/run_ahead.h @@ -10,6 +10,9 @@ void runahead_destroy(void); void run_ahead(int runAheadCount, bool useSecondary); +bool want_fast_savestate(void); +bool get_hard_disable_audio(void); + RETRO_END_DECLS #endif From 36d33deb30fc2c2d6804d84b2f4d04534b48f24a Mon Sep 17 00:00:00 2001 From: Dwedit Date: Fri, 30 Mar 2018 20:41:52 -0500 Subject: [PATCH 096/517] Add message about load state expected to succeed. --- libretro-common/include/libretro.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index f866b0bc77..7d0118526f 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1144,6 +1144,7 @@ struct retro_led_interface * Fast Savestates: * * Guaranteed to be created by the same binary that will load them. * * Will not be written to or read from the disk. + * * Suggest that the core assumes loading state will succeed. * * Suggest that the core updates its memory buffers in-place if possible. * * Suggest that the core skips clearing memory. * * Suggest that the core skips resetting the system. From d65bd90e67f43ab66785f67d4a5f3c4412b569d1 Mon Sep 17 00:00:00 2001 From: gblues Date: Fri, 30 Mar 2018 18:57:34 -0700 Subject: [PATCH 097/517] Fix GC pad button mapping --- input/common/hid/device_wiiu_gca.c | 55 ++++++------------------------ input/input_autodetect_builtin.c | 34 +++++++++++------- 2 files changed, 31 insertions(+), 58 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 7556305f6d..1275c958d8 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -101,8 +101,6 @@ static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size) return; } - //RARCH_LOG_BUFFER(buffer, size); - memcpy(instance->device_state, buffer, size); update_pad_state(instance); } @@ -165,7 +163,6 @@ static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port return NULL; } - RARCH_LOG("[gca]: registering pad in port %d to slot %d\n", port+1, slot); result = &(hid_instance.pad_list[slot]); result->iface = &wiiu_gca_pad_connection; result->data = result->iface->init(instance, slot, hid_instance.os_driver); @@ -184,6 +181,7 @@ static void unregister_pad(wiiu_gca_instance_t *instance, int slot) instance->pads[slot] = NULL; pad->iface->deinit(pad->data); pad->data = NULL; + pad->iface = NULL; pad->connected = false; } @@ -241,20 +239,6 @@ static void wiiu_gca_get_buttons(void *data, retro_bits_t *state) } } -static void log_bitmask(uint32_t bits) -{ - char buf[33]; - int i; - - for(i = 0; i < 32; i++) - { - buf[i] = (bits & (1 << i)) ? '1' : '0'; - } - buf[32] = '\0'; - - RARCH_LOG("pressed_keys: %s\n", buf); -} - /** * The USB packet provides a 9-byte data packet for each pad. * @@ -269,38 +253,11 @@ static void wiiu_gca_packet_handler(void *data, uint8_t *packet, uint16_t size) gca_pad_t *pad = (gca_pad_t *)data; uint32_t i, pressed_keys; - static const uint32_t button_mapping[12] = - { - RETRO_DEVICE_ID_JOYPAD_A, - RETRO_DEVICE_ID_JOYPAD_B, - RETRO_DEVICE_ID_JOYPAD_X, - RETRO_DEVICE_ID_JOYPAD_Y, - RETRO_DEVICE_ID_JOYPAD_LEFT, - RETRO_DEVICE_ID_JOYPAD_RIGHT, - RETRO_DEVICE_ID_JOYPAD_DOWN, - RETRO_DEVICE_ID_JOYPAD_UP, - RETRO_DEVICE_ID_JOYPAD_START, - RETRO_DEVICE_ID_JOYPAD_SELECT, - RETRO_DEVICE_ID_JOYPAD_R, - RETRO_DEVICE_ID_JOYPAD_L, - }; - if(!pad || !packet || size > sizeof(pad->data)) return; -/* RARCH_LOG_BUFFER(packet, size); */ - memcpy(pad->data, packet, size); - pad->buttons = 0; - pressed_keys = pad->data[1] | (pad->data[2] << 8); - - log_bitmask(pressed_keys); - - for(i = 0; i < 12; i++) - { - pad->buttons |= (pressed_keys & (1 << i)) ? - (1 << button_mapping[i]) : 0; - } + pad->buttons = pad->data[1] | (pad->data[2] << 8); } static void wiiu_gca_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) @@ -345,6 +302,14 @@ static const char *wiiu_gca_get_name(void *data) return "GameCube Controller"; } +/** + * Button bitmask values: + * 0x0001 - A 0x0010 - left 0x0100 - Start/Pause + * 0x0002 - B 0x0020 - right 0x0200 - Z + * 0x0004 - X 0x0040 - down 0x0400 - R + * 0x0008 - Y 0x0080 - up 0x0800 - L + */ + static bool wiiu_gca_button(void *data, uint16_t joykey) { gca_pad_t *pad = (gca_pad_t *)data; diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index 45bbed0775..75b9f0d3c6 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -229,18 +229,26 @@ DECL_AXIS(r_y_minus, +3) #ifdef WIIU #define WIIUINPUT_GAMECUBE_DEFAULT_BINDS \ -DECL_BTN_EX(b, 0, "B") \ -DECL_BTN_EX(y, 1, "Y") \ -DECL_BTN_EX(select, 2, "Z") \ -DECL_BTN_EX(start, 3, "Start/Pause") \ -DECL_BTN_EX(up, 4, "D-Pad Up") \ -DECL_BTN_EX(down, 5, "D-Pad Down") \ -DECL_BTN_EX(left, 6, "D-Pad Left") \ -DECL_BTN_EX(right, 7, "D-Pad Right") \ -DECL_BTN_EX(a, 8, "A") \ -DECL_BTN_EX(x, 9, "X") \ -DECL_BTN_EX(l, 10, "L") \ -DECL_BTN_EX(r, 11, "R") +DECL_BTN_EX(a, 0x0001, "A") \ +DECL_BTN_EX(b, 0x0002, "B") \ +DECL_BTN_EX(x, 0x0004, "X") \ +DECL_BTN_EX(y, 0x0008, "Y") \ +DECL_BTN_EX(select, 0x0200, "Z") \ +DECL_BTN_EX(start, 0x0100, "Start/Pause") \ +DECL_BTN_EX(l, 0x0800, "L Trigger") \ +DECL_BTN_EX(r, 0x0400, "R Trigger") \ +DECL_BTN_EX(left, 0x0010, "D-Pad Left") \ +DECL_BTN_EX(right, 0x0020, "D-Pad Right") \ +DECL_BTN_EX(down, 0x0040, "D-Pad Down") \ +DECL_BTN_EX(up, 0x0080, "D-Pad Up") \ +DECL_AXIS_EX(l_x_plus, +1, "Analog right") \ +DECL_AXIS_EX(l_x_minus, -1, "Analog left") \ +DECL_AXIS_EX(l_y_plus, +0, "Analog up") \ +DECL_AXIS_EX(l_y_minus, -0, "Analog down") \ +DECL_AXIS_EX(r_x_plus, +3, "C-stick right") \ +DECL_AXIS_EX(r_x_minus, -3, "C-stick left") \ +DECL_AXIS_EX(r_y_plus, +2, "C-stick up") \ +DECL_AXIS_EX(r_y_minus, -2, "C-stick down") #define WIIUINPUT_GAMEPAD_DEFAULT_BINDS \ DECL_BTN_EX(menu_toggle, 1, "Home") \ @@ -626,7 +634,7 @@ const char* const input_builtin_autoconfs[] = DECL_AUTOCONF_DEVICE(PAD_NAME_NUNCHUK, "wiiu", WIIUINPUT_NUNCHUK_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE(PAD_NAME_CLASSIC, "wiiu", WIIUINPUT_CLASSIC_CONTROLLER_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE(PAD_NAME_HID, "wiiu", WIIUINPUT_GAMEPAD_DEFAULT_BINDS), - DECL_AUTOCONF_DEVICE("GameCube Controller", "wiiu", WIIUINPUT_GAMEPAD_DEFAULT_BINDS), + DECL_AUTOCONF_DEVICE("GameCube Controller", "wiiu", WIIUINPUT_GAMECUBE_DEFAULT_BINDS), #endif #ifdef __CELLOS_LV2__ DECL_AUTOCONF_DEVICE("SixAxis Controller", "ps3", PS3INPUT_DEFAULT_BINDS), From 9164bcc244312aaa0404ed0d329cc78bebc4fbf6 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 31 Mar 2018 04:22:18 +0200 Subject: [PATCH 098/517] Add HAVE_RUNAHEAD to Android --- pkg/android/phoenix/jni/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/android/phoenix/jni/Android.mk b/pkg/android/phoenix/jni/Android.mk index 20cb44d57a..bd87b9d0bf 100644 --- a/pkg/android/phoenix/jni/Android.mk +++ b/pkg/android/phoenix/jni/Android.mk @@ -67,7 +67,7 @@ else DEFINES += -DHAVE_OPENGLES2 endif -DEFINES += -DRARCH_MOBILE -DHAVE_GRIFFIN -DHAVE_STB_VORBIS -DHAVE_LANGEXTRA -DANDROID -DHAVE_DYNAMIC -DHAVE_OPENGL -DHAVE_OVERLAY -DHAVE_OPENGLES -DGLSL_DEBUG -DHAVE_DYLIB -DHAVE_EGL -DHAVE_GLSL -DHAVE_MENU -DHAVE_RGUI -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DINLINE=inline -DHAVE_THREADS -D__LIBRETRO__ -DHAVE_RSOUND -DHAVE_NETWORKGAMEPAD -DHAVE_NETWORKING -DRARCH_INTERNAL -DHAVE_FILTERS_BUILTIN -DHAVE_MATERIALUI -DHAVE_XMB -DHAVE_SHADERPIPELINE -DHAVE_LIBRETRODB -DHAVE_STB_FONT -DHAVE_IMAGEVIEWER -DHAVE_UPDATE_ASSETS -DHAVE_CC_RESAMPLER -DHAVE_MINIUPNPC -DHAVE_BUILTINMINIUPNPC -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -DHAVE_KEYMAPPER -DHAVE_NETWORKGAMEPAD -DHAVE_FLAC -DHAVE_CHD +DEFINES += -DRARCH_MOBILE -DHAVE_GRIFFIN -DHAVE_STB_VORBIS -DHAVE_LANGEXTRA -DANDROID -DHAVE_DYNAMIC -DHAVE_OPENGL -DHAVE_OVERLAY -DHAVE_OPENGLES -DGLSL_DEBUG -DHAVE_DYLIB -DHAVE_EGL -DHAVE_GLSL -DHAVE_MENU -DHAVE_RGUI -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DINLINE=inline -DHAVE_THREADS -D__LIBRETRO__ -DHAVE_RSOUND -DHAVE_NETWORKGAMEPAD -DHAVE_NETWORKING -DRARCH_INTERNAL -DHAVE_FILTERS_BUILTIN -DHAVE_MATERIALUI -DHAVE_XMB -DHAVE_SHADERPIPELINE -DHAVE_LIBRETRODB -DHAVE_STB_FONT -DHAVE_IMAGEVIEWER -DHAVE_UPDATE_ASSETS -DHAVE_CC_RESAMPLER -DHAVE_MINIUPNPC -DHAVE_BUILTINMINIUPNPC -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -DHAVE_KEYMAPPER -DHAVE_NETWORKGAMEPAD -DHAVE_FLAC -DHAVE_CHD -DHAVE_RUNAHEAD DEFINES += -DWANT_IFADDRS ifeq ($(HAVE_VULKAN),1) From 15472eb184b31d3768639789f3c47fe4063ae7eb Mon Sep 17 00:00:00 2001 From: Dwedit Date: Sat, 31 Mar 2018 00:32:41 -0500 Subject: [PATCH 099/517] Stop savestate transmission in netplay --- runahead/run_ahead.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 74551b8a1b..5c4305bd54 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -354,7 +354,9 @@ static bool runahead_load_state(void) bool lastDirty = input_is_dirty; bool okay; set_fast_savestate(); - okay = core_unserialize(serialize_info); + /* calling core_unserialize has side effects with netplay (it triggers transmitting your save state) + call retro_unserialize directly from the core instead */ + okay = current_core.retro_unserialize(serialize_info->data_const, serialize_info->size); unset_fast_savestate(); input_is_dirty = lastDirty; From 6b77a6629817b5e013ed41a8f19b519352c73902 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Sat, 31 Mar 2018 00:55:37 -0500 Subject: [PATCH 100/517] get rid of the unnecessary typedef --- runahead/run_ahead.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 5c4305bd54..f90736abeb 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -96,8 +96,7 @@ static void runahead_save_state_list_rotate(void) static function_t originalRetroDeinit = NULL; static function_t originalRetroUnload = NULL; -typedef struct retro_core_t _retro_core_t; -extern _retro_core_t current_core; +extern struct retro_core_t current_core; extern struct retro_callbacks retro_ctx; static void remove_hooks(void) From 39e4167df65756591f3ab74b7c8bad854926fddd Mon Sep 17 00:00:00 2001 From: gblues Date: Fri, 30 Mar 2018 23:00:14 -0700 Subject: [PATCH 101/517] Start work on DualShock 3 driver == DETAILS The WiiU GC adapter is working! Next up: DualShock 3 I have the skeleton of the driver started, need to work out the activation packet. == TESTING The DS3 driver is broke as hell right now. --- input/common/hid/device_ds3.c | 114 +++++++++++++++++++++++---- input/common/hid/device_wiiu_gca.c | 5 +- input/common/hid/hid_device_driver.c | 21 ++++- input/common/hid/hid_device_driver.h | 3 +- 4 files changed, 121 insertions(+), 22 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index 8b156d2ef5..3c1a805d60 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -16,38 +16,124 @@ #include "hid_device_driver.h" -struct ds3_instance { - hid_driver_t *driver; - void *handle; +#define DS3_ACTIVATION_REPORT_ID 0xf4 +#define DS3_RUMBLE_REPORT_ID 0x01 + +typedef struct ds3_instance { + void *handle; + joypad_connection_t *pad; +} ds3_instance_t; + +static uint8_t activation_packet[] = { + 0x42, 0x0c, 0x00, 0x00 }; +extern pad_connection_interface_t ds3_pad_connection; + static void *ds3_init(void *handle) { - return NULL; + ds3_instance_t *instance; + + instance = (ds3_instance_t *)calloc(1, sizeof(ds3_instance_t)); + if(!instance) + goto error; + + instance->handle = handle; + +/* TODO: do whatever is needed so that the read loop doesn't bomb out */ + + instance->pad = hid_pad_register(instance, &ds3_pad_connection); + if(!instance->pad) + goto error; + + return instance; + + error: + if(instance) + free(instance); + return NULL; } static void ds3_free(void *data) { - struct ds3_instance *instance = (struct ds3_instance *)data; - if(!instance) - return; + ds3_instance_t *instance = (ds3_instance_t *)data; - free(instance); + if(instance) + free(instance); } static void ds3_handle_packet(void *data, uint8_t *buffer, size_t size) { + ds3_instance_t *instance = (ds3_instance_t *)data; } static bool ds3_detect(uint16_t vendor_id, uint16_t product_id) { - return vendor_id == VID_SONY && product_id == PID_SONY_DS3; + return vendor_id == VID_SONY && product_id == PID_SONY_DS3; } hid_device_t ds3_hid_device = { - ds3_init, - ds3_free, - ds3_handle_packet, - ds3_detect, - "Sony DualShock 3" + ds3_init, + ds3_free, + ds3_handle_packet, + ds3_detect, + "Sony DualShock 3" +}; + +/** + * pad interface implementation + */ + +static void *ds3_pad_init(void *data, uint32_t slot, hid_driver_t *driver) +{ + return data; +} + +static void ds3_pad_deinit(void *data) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; +} + +static void ds3_get_buttons(void *data, retro_bits_t *state) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; +} + +static void ds3_packet_handler(void *data, uint8_t *packet, uint16_t size) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; +} + +static void ds3_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; +} + +static int16_t ds3_get_axis(void *data, unsigned axis) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; + return 0; +} + +static const char *ds3_get_name(void *data) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; + return "Sony DualShock 3"; +} + +static bool ds3_button(void *data, uint16_t joykey) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; + return false; +} + +pad_connection_interface_t ds3_pad_connection = { + ds3_pad_init, + ds3_pad_deinit, + ds3_packet_handler, + ds3_set_rumble, + ds3_get_buttons, + ds3_get_axis, + ds3_get_name, + ds3_button }; diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 1275c958d8..1d50c513ee 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -44,7 +44,6 @@ typedef struct gca_pad_data static void update_pad_state(wiiu_gca_instance_t *instance); -static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port); static void unregister_pad(wiiu_gca_instance_t *instance, int port); extern pad_connection_interface_t wiiu_gca_pad_connection; @@ -131,7 +130,7 @@ static void update_pad_state(wiiu_gca_instance_t *instance) if(pad == NULL) { RARCH_LOG("[gca]: Gamepad at port %d connected.\n", port+1); - instance->pads[port] = register_pad(instance, port); + instance->pads[port] = hid_pad_register(instance, &wiiu_gca_pad_connection); pad = instance->pads[port]; if(pad == NULL) { @@ -146,6 +145,7 @@ static void update_pad_state(wiiu_gca_instance_t *instance) } } +/* static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port) { int slot; joypad_connection_t *result; @@ -171,6 +171,7 @@ static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port return result; } +*/ static void unregister_pad(wiiu_gca_instance_t *instance, int slot) { diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c index 13bbf89d40..587f0dc33d 100644 --- a/input/common/hid/hid_device_driver.c +++ b/input/common/hid/hid_device_driver.c @@ -36,12 +36,25 @@ hid_device_t *hid_device_driver_lookup(uint16_t vendor_id, uint16_t product_id) return NULL; } -void hid_pad_connect(hid_driver_instance_t *instance, int pad) +joypad_connection_t *hid_pad_register(void *pad_handle, pad_connection_interface_t *iface) { - if(!instance || !instance->pad_driver) - return; + int slot; + joypad_connection_t *result; - input_pad_connect(pad, instance->pad_driver); + if(!pad_handle) + return NULL; + + slot = pad_connection_find_vacant_pad(hid_instance.pad_list); + if(slot < 0) + return NULL; + + result = &(hid_instance.pad_list[slot]); + result->iface = iface; + result->data = iface->init(pad_handle, slot, hid_instance.os_driver); + result->connected = true; + input_pad_connect(slot, hid_instance.pad_driver); + + return result; } /** diff --git a/input/common/hid/hid_device_driver.h b/input/common/hid/hid_device_driver.h index 8cd9f19944..715fd7cfea 100644 --- a/input/common/hid/hid_device_driver.h +++ b/input/common/hid/hid_device_driver.h @@ -36,8 +36,7 @@ extern hid_device_t ds4_hid_device; extern hid_driver_instance_t hid_instance; hid_device_t *hid_device_driver_lookup(uint16_t vendor_id, uint16_t product_id); - -void hid_pad_connect(hid_driver_instance_t *instance, int pad); +joypad_connection_t *hid_pad_register(void *pad_handle, pad_connection_interface_t *iface); bool hid_init(hid_driver_instance_t *instance, hid_driver_t *hid_driver, input_device_driver_t *pad_driver, unsigned slots); void hid_deinit(hid_driver_instance_t *instance); From 80b8410b0fed3745df67dd42992ad6d59a11be6b Mon Sep 17 00:00:00 2001 From: Dwedit Date: Sat, 31 Mar 2018 12:15:14 -0500 Subject: [PATCH 102/517] Forgot to initialize controller port map, this caused VBA-M to crash when creating the secondary core. Now fixed. --- core_impl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/core_impl.c b/core_impl.c index 5acc659943..7c2585a0c2 100644 --- a/core_impl.c +++ b/core_impl.c @@ -292,6 +292,7 @@ bool core_load_game(retro_ctx_load_content_info_t *load_info) #ifdef HAVE_RUNAHEAD set_load_content_info(load_info); + clear_controller_port_map(); #endif content_get_status(&contentless, &is_inited); From 2cf89feb868393a912549d8b37e2b7c17b5d2397 Mon Sep 17 00:00:00 2001 From: gblues Date: Sat, 31 Mar 2018 22:25:30 -0700 Subject: [PATCH 103/517] Code clean-up == DETAILS Now that I have a working implementation, it's time to tidy up a bit: - there was no need for the HID subsystem's object data to have a reference to the global hid state (since it's global), so removed it. - refactored the users of that member to use the global state, defining reusable macros. - reorganized the information in *.h files - removing the hid state also made the constructor changes to the hid driver unneeded, so I reverted those changes. == TESTING Confirmed clean build. Haven't tested the build yet to make sure everything still works, though. --- input/common/hid/hid_device_driver.c | 38 +----- input/drivers_hid/btstack_hid.c | 7 +- input/drivers_hid/iohidmanager_hid.c | 7 +- input/drivers_hid/libusb_hid.c | 7 +- input/drivers_hid/null_hid.c | 4 +- input/drivers_hid/wiiusb_hid.c | 6 +- input/drivers_joypad/wiiu_joypad.c | 8 +- input/include/hid_driver.h | 4 +- input/input_autodetect_builtin.c | 2 +- wiiu/include/wiiu/pad_driver.h | 186 --------------------------- wiiu/include/wiiu/pad_strings.h | 32 +++++ wiiu/input/hidpad_driver.c | 5 +- wiiu/input/kpad_driver.c | 2 +- wiiu/input/pad_functions.c | 2 +- wiiu/input/wiiu_hid.c | 10 +- wiiu/input/wiiu_hid.h | 69 +++++++++- wiiu/input/wiiu_hid_types.h | 28 ++++ wiiu/input/wiiu_input.h | 72 +++++++++++ wiiu/input/wpad_driver.c | 4 +- 19 files changed, 229 insertions(+), 264 deletions(-) delete mode 100644 wiiu/include/wiiu/pad_driver.h create mode 100644 wiiu/include/wiiu/pad_strings.h create mode 100644 wiiu/input/wiiu_hid_types.h create mode 100644 wiiu/input/wiiu_input.h diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c index 587f0dc33d..3792e2c737 100644 --- a/input/common/hid/hid_device_driver.c +++ b/input/common/hid/hid_device_driver.c @@ -76,7 +76,7 @@ bool hid_init(hid_driver_instance_t *instance, return false; RARCH_LOG("[hid]: initializing HID subsystem driver...\n"); - instance->os_driver_data = hid_driver->init(instance); + instance->os_driver_data = hid_driver->init(); if(!instance->os_driver_data) return false; @@ -122,39 +122,3 @@ void hid_deinit(hid_driver_instance_t *instance) RARCH_LOG("[hid]: wiping instance data...\n"); memset(instance, 0, sizeof(hid_driver_instance_t)); } - -static void hid_device_log_buffer(uint8_t *data, uint32_t len) -{ -#if 0 - int i, offset; - int padding = len % 0x0F; - uint8_t buf[16]; - - RARCH_LOG("%d bytes read:\n", len); - - for(i = 0, offset = 0; i < len; i++) - { - buf[offset] = data[i]; - offset++; - if(offset == 16) - { - offset = 0; - RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); - } - } - - if(padding) - { - for(i = padding; i < 16; i++) - buf[i] = 0xff; - - RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); - } - - RARCH_LOG("=================================\n"); - #endif -} diff --git a/input/drivers_hid/btstack_hid.c b/input/drivers_hid/btstack_hid.c index 68f20ea666..1d277484aa 100644 --- a/input/drivers_hid/btstack_hid.c +++ b/input/drivers_hid/btstack_hid.c @@ -1439,17 +1439,14 @@ static void btstack_hid_free(const void *data) free(hid); } -static void *btstack_hid_init(joypad_connection_t *connections) +static void *btstack_hid_init(void) { btstack_hid_t *hid = (btstack_hid_t*)calloc(1, sizeof(btstack_hid_t)); if (!hid) goto error; - if(connections == NULL) - connections = pad_connection_init(MAX_USERS); - - hid->slots = connections; + hid->slots = pad_connection_init(MAX_USERS); if (!hid->slots) goto error; diff --git a/input/drivers_hid/iohidmanager_hid.c b/input/drivers_hid/iohidmanager_hid.c index ad60d4c969..faaf84eceb 100644 --- a/input/drivers_hid/iohidmanager_hid.c +++ b/input/drivers_hid/iohidmanager_hid.c @@ -854,7 +854,7 @@ static int iohidmanager_hid_manager_set_device_matching( return 0; } -static void *iohidmanager_hid_init(joypad_connection_t *connections) +static void *iohidmanager_hid_init(void) { iohidmanager_hid_t *hid_apple = (iohidmanager_hid_t*) calloc(1, sizeof(*hid_apple)); @@ -862,10 +862,7 @@ static void *iohidmanager_hid_init(joypad_connection_t *connections) if (!hid_apple) goto error; - if (connections == NULL) - connections = pad_connection_init(MAX_USERS); - - hid_apple->slots = connections; + hid_apple->slots = pad_connection_init(MAX_USERS); if (!hid_apple->slots) goto error; diff --git a/input/drivers_hid/libusb_hid.c b/input/drivers_hid/libusb_hid.c index f06adb5366..44580359fa 100644 --- a/input/drivers_hid/libusb_hid.c +++ b/input/drivers_hid/libusb_hid.c @@ -546,7 +546,7 @@ static void poll_thread(void *data) } } -static void *libusb_hid_init(joypad_connection_t *connections) +static void *libusb_hid_init(void) { unsigned i, count; int ret; @@ -578,10 +578,7 @@ static void *libusb_hid_init(joypad_connection_t *connections) hid->can_hotplug = 0; #endif - if (connections == NULL) - connections = pad_connection_init(MAX_USERS); - - hid->slots = connections; + hid->slots = pad_connection_init(MAX_USERS); if (!hid->slots) goto error; diff --git a/input/drivers_hid/null_hid.c b/input/drivers_hid/null_hid.c index e7cf00b580..125ac54739 100644 --- a/input/drivers_hid/null_hid.c +++ b/input/drivers_hid/null_hid.c @@ -76,10 +76,8 @@ static int16_t null_hid_joypad_axis(void *data, unsigned port, uint32_t joyaxis) return 0; } -static void *null_hid_init(hid_driver_instance_t *instance) +static void *null_hid_init(void) { - (void)instance; - return (null_hid_t*)calloc(1, sizeof(null_hid_t)); } diff --git a/input/drivers_hid/wiiusb_hid.c b/input/drivers_hid/wiiusb_hid.c index 6e3a768bca..397f0a0a64 100644 --- a/input/drivers_hid/wiiusb_hid.c +++ b/input/drivers_hid/wiiusb_hid.c @@ -574,15 +574,15 @@ static void wiiusb_hid_free(const void *data) free(hid); } -static void *wiiusb_hid_init(joypad_connection_t *connections) +static void *wiiusb_hid_init(void) { + joypad_connection_t *connections = NULL; wiiusb_hid_t *hid = (wiiusb_hid_t*)calloc(1, sizeof(*hid)); if (!hid) goto error; - if(connections == NULL) - connections = pad_connection_init(MAX_USERS); + connections = pad_connection_init(MAX_USERS); if (!connections) goto error; diff --git a/input/drivers_joypad/wiiu_joypad.c b/input/drivers_joypad/wiiu_joypad.c index aa9c3ab277..dc0ab8fe94 100644 --- a/input/drivers_joypad/wiiu_joypad.c +++ b/input/drivers_joypad/wiiu_joypad.c @@ -14,7 +14,7 @@ * If not, see . */ -#include +#include "../../wiiu/input/wiiu_input.h" #include "wiiu_dbg.h" @@ -40,10 +40,10 @@ static input_device_driver_t *get_driver_for_pad(unsigned pad) { if(wpad_driver.query_pad(pad)) return &wpad_driver; -/* + if(kpad_driver.query_pad(pad)) return &kpad_driver; -*/ + #ifdef WIIU_HID return &hidpad_driver; #else @@ -135,7 +135,7 @@ static void wiiu_joypad_poll(void) static const char* wiiu_joypad_name(unsigned pad) { if(!wiiu_joypad_query_pad(pad)) - return "Snuffleupagus"; + return "N/A"; return pad_drivers[pad]->name(pad); } diff --git a/input/include/hid_driver.h b/input/include/hid_driver.h index 1eee8a3f85..619f491688 100644 --- a/input/include/hid_driver.h +++ b/input/include/hid_driver.h @@ -35,7 +35,7 @@ struct hid_driver { - void *(*init)(hid_driver_instance_t *); + void *(*init)(void); bool (*query_pad)(void *handle, unsigned pad); void (*free)(const void *handle); bool (*button)(void *handle, unsigned pad, uint16_t button); @@ -62,6 +62,8 @@ struct hid_driver hid_instance.os_driver_data, pad) #define HID_POLL() hid_instance.os_driver->poll( \ hid_instance.os_driver_data) +#define HID_MAX_SLOT() hid_instance.max_slot +#define HID_PAD_CONNECTION_PTR(slot) &(hid_instance.pad_list[(slot)]) diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index 75b9f0d3c6..2a237e8831 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -29,7 +29,7 @@ #endif #ifdef WIIU -#include +#include #endif #define DECL_BTN(btn, bind) "input_" #btn "_btn = " #bind "\n" diff --git a/wiiu/include/wiiu/pad_driver.h b/wiiu/include/wiiu/pad_driver.h deleted file mode 100644 index 067bc34d67..0000000000 --- a/wiiu/include/wiiu/pad_driver.h +++ /dev/null @@ -1,186 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2014-2017 - Ali Bouhlel - * Copyright (C) 2011-2017 - Daniel De Matteis - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#ifndef __PAD_DRIVER__H -#define __PAD_DRIVER__H - -#ifdef HAVE_CONFIG_H -#include "../../config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../input/input_driver.h" -#include "../../input/common/hid/hid_device_driver.h" -#include "../../tasks/tasks_internal.h" -#include "../../input/connect/joypad_connection.h" -#include "../../retroarch.h" -#include "../../verbosity.h" -#include "../../command.h" -#include "../../gfx/video_driver.h" - -/** - * Magic button sequence that triggers an exit. Useful for if the visuals are - * corrupted, but won't work in the case of a hard lock. - */ -#define PANIC_BUTTON_MASK (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L) - -/** - * Applies a standard transform to the Wii U gamepad's analog stick. - * No idea where 0x7ff0 comes from. - */ - -#define WIIU_ANALOG_FACTOR 0x7ff0 -#define WIIU_READ_STICK(stick) ((stick) * WIIU_ANALOG_FACTOR) - -/** - * the wiimote driver uses these to delimit which pads correspond to the - * wiimotes. - */ -#define PAD_GAMEPAD 0 -#define WIIU_WIIMOTE_CHANNELS 4 - -/** - * These are used by the wiimote driver to identify the wiimote configuration - * attached to the channel. - */ -/* wiimote with Wii U Pro controller */ -#define WIIMOTE_TYPE_PRO 0x1f -/* wiimote with Classic Controller */ -#define WIIMOTE_TYPE_CLASSIC 0x02 -/* wiimote with nunchuk */ -#define WIIMOTE_TYPE_NUNCHUK 0x01 -/* wiimote plus (no accessory attached) */ -#define WIIMOTE_TYPE_WIIPLUS 0x00 -/* wiimote not attached on this channel */ -#define WIIMOTE_TYPE_NONE 0xFD - -/** - * These are used to map pad names to controller mappings. You can - * change these relatively free-form. - */ - -#define PAD_NAME_WIIU_GAMEPAD "WiiU Gamepad" -#define PAD_NAME_WIIU_PRO "WiiU Pro Controller" -#define PAD_NAME_WIIMOTE "Wiimote Controller" -#define PAD_NAME_NUNCHUK "Wiimote+Nunchuk Controller" -#define PAD_NAME_CLASSIC "Classic Controller" -#define PAD_NAME_HID "HID Controller" - -/** - * The Wii U gamepad and wiimotes have 3 sets of x/y axes. The third - * is used by the gamepad for the touchpad driver; the wiimotes is - * currently unimplemented, but could be used for future IR pointer - * support. - */ -#define WIIU_DEVICE_INDEX_TOUCHPAD 2 - -typedef struct _axis_data axis_data; - -struct _axis_data { - int32_t axis; - bool is_negative; -}; - -typedef struct _wiiu_pad_functions wiiu_pad_functions_t; - -struct _wiiu_pad_functions { - int16_t (*get_axis_value)(int32_t axis, int16_t state[3][2], bool is_negative); - void (*set_axis_value)(int16_t state[3][2], int16_t left_x, int16_t left_y, - int16_t right_x, int16_t right_y, int16_t touch_x, int16_t touch_y); - void (*read_axis_data)(uint32_t axis, axis_data *data); - void (*connect)(unsigned pad, input_device_driver_t *driver); -}; - -/** - * HID driver data structures - */ - -typedef struct wiiu_hid { - /* used to register for HID notifications */ - HIDClient *client; - /* pointer to HID driver state */ - hid_driver_instance_t *driver; - /* size of connections list */ - unsigned connections_size; - /* thread state data for HID polling thread */ - OSThread *polling_thread; - /* stack space for polling thread */ - void *polling_thread_stack; - /* watch variable to tell the polling thread to terminate */ - volatile bool polling_thread_quit; -} wiiu_hid_t; - -typedef struct wiiu_adapter wiiu_adapter_t; - -struct wiiu_adapter { - wiiu_adapter_t *next; - hid_device_t *driver; - void *driver_handle; - wiiu_hid_t *hid; - uint8_t state; - uint8_t *rx_buffer; - int32_t rx_size; - uint8_t *tx_buffer; - int32_t tx_size; - uint32_t handle; - uint8_t interface_index; -}; - -typedef struct wiiu_attach wiiu_attach_event; - -struct wiiu_attach { - wiiu_attach_event *next; - hid_device_t *driver; - uint32_t type; - uint32_t handle; - uint16_t vendor_id; - uint16_t product_id; - uint8_t interface_index; - uint8_t is_keyboard; - uint8_t is_mouse; - uint16_t max_packet_size_rx; - uint16_t max_packet_size_tx; -}; - -typedef struct _wiiu_event_list wiiu_event_list; -typedef struct _wiiu_adapter_list wiiu_adapter_list; - -struct _wiiu_event_list { - OSFastMutex lock; - wiiu_attach_event *list; -}; - -struct _wiiu_adapter_list { - OSFastMutex lock; - wiiu_adapter_t *list; -}; - -extern wiiu_pad_functions_t pad_functions; -extern input_device_driver_t wiiu_joypad; -extern input_device_driver_t wpad_driver; -extern input_device_driver_t kpad_driver; -extern input_device_driver_t hidpad_driver; -extern hid_driver_t wiiu_hid; - -#endif /* __PAD_DRIVER__H */ diff --git a/wiiu/include/wiiu/pad_strings.h b/wiiu/include/wiiu/pad_strings.h new file mode 100644 index 0000000000..e28a9036e6 --- /dev/null +++ b/wiiu/include/wiiu/pad_strings.h @@ -0,0 +1,32 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2014-2017 - Ali Bouhlel + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __PAD_DRIVER__H +#define __PAD_DRIVER__H + +/** + * These are used to map pad names to controller mappings. You can + * change these relatively free-form. + */ + +#define PAD_NAME_WIIU_GAMEPAD "WiiU Gamepad" +#define PAD_NAME_WIIU_PRO "WiiU Pro Controller" +#define PAD_NAME_WIIMOTE "Wiimote Controller" +#define PAD_NAME_NUNCHUK "Wiimote+Nunchuk Controller" +#define PAD_NAME_CLASSIC "Classic Controller" +#define PAD_NAME_HID "HID Controller" + +#endif /* __PAD_DRIVER__H */ diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c index 97f05e9d06..896ba4b2d4 100644 --- a/wiiu/input/hidpad_driver.c +++ b/wiiu/input/hidpad_driver.c @@ -14,9 +14,8 @@ * If not, see . */ -#include -#include "../../input/include/hid_driver.h" -#include "../../input/common/hid/hid_device_driver.h" +#include "wiiu_input.h" +#include "wiiu_hid.h" static bool hidpad_init(void *data); static bool hidpad_query_pad(unsigned pad); diff --git a/wiiu/input/kpad_driver.c b/wiiu/input/kpad_driver.c index 51d203cec0..faebe1143e 100644 --- a/wiiu/input/kpad_driver.c +++ b/wiiu/input/kpad_driver.c @@ -20,7 +20,7 @@ * controllers. */ -#include +#include "wiiu_input.h" static bool kpad_init(void *data); static bool kpad_query_pad(unsigned pad); diff --git a/wiiu/input/pad_functions.c b/wiiu/input/pad_functions.c index 132bffa146..ad2c383090 100644 --- a/wiiu/input/pad_functions.c +++ b/wiiu/input/pad_functions.c @@ -14,7 +14,7 @@ * If not, see . */ -#include +#include "wiiu_input.h" enum wiiu_pad_axes { AXIS_LEFT_ANALOG_X, diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index 44c2822305..b289f648f4 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -22,10 +22,10 @@ static wiiu_adapter_list adapters; static bool wiiu_hid_joypad_query(void *data, unsigned slot) { wiiu_hid_t *hid = (wiiu_hid_t *)data; - if (!hid || !hid->driver) + if (!hid) return false; - return slot < hid->driver->max_slot; + return slot < HID_MAX_SLOT(); } static joypad_connection_t *get_pad(wiiu_hid_t *hid, unsigned slot) @@ -33,7 +33,7 @@ static joypad_connection_t *get_pad(wiiu_hid_t *hid, unsigned slot) if(!wiiu_hid_joypad_query(hid, slot)) return NULL; - joypad_connection_t *result = &(hid->driver->pad_list[slot]); + joypad_connection_t *result = HID_PAD_CONNECTION_PTR(slot); if(!result || !result->connected || !result->iface || !result->data) return NULL; @@ -90,7 +90,7 @@ static int16_t wiiu_hid_joypad_axis(void *data, unsigned slot, uint32_t joyaxis) return pad->iface->get_axis(pad->data, joyaxis); } -static void *wiiu_hid_init(hid_driver_instance_t *driver) +static void *wiiu_hid_init(void) { RARCH_LOG("[hid]: initializing HID subsystem\n"); wiiu_hid_t *hid = new_hid(); @@ -99,8 +99,6 @@ static void *wiiu_hid_init(hid_driver_instance_t *driver) if (!hid || !client) goto error; - hid->driver = driver; - wiiu_hid_init_lists(); start_polling_thread(hid); if (!hid->polling_thread) diff --git a/wiiu/input/wiiu_hid.h b/wiiu/input/wiiu_hid.h index 59ad898fea..892e1d3151 100644 --- a/wiiu/input/wiiu_hid.h +++ b/wiiu/input/wiiu_hid.h @@ -17,8 +17,9 @@ #ifndef __WIIU_HID__H #define __WIIU_HID__H -#include -#include "../../input/include/hid_driver.h" +#include "wiiu_hid_types.h" + +#include "wiiu_input.h" #define DEVICE_UNUSED 0 #define DEVICE_USED 1 @@ -28,6 +29,70 @@ #define ADAPTER_STATE_READING 2 #define ADAPTER_STATE_DONE 3 +struct wiiu_hid { + /* used to register for HID notifications */ + HIDClient *client; + /* thread state data for the HID input polling thread */ + OSThread *polling_thread; + /* stack space for polling thread */ + void *polling_thread_stack; + /* watch variable for telling the polling thread to terminate */ + volatile bool polling_thread_quit; +}; + +/** + * Each HID device attached to the WiiU gets its own adapter, which + * connects the HID subsystem with the HID device driver. + */ +struct wiiu_adapter { + wiiu_adapter_t *next; + hid_device_t *driver; + void *driver_handle; + wiiu_hid_t *hid; + uint8_t state; + uint8_t *rx_buffer; + int32_t rx_size; + uint8_t *tx_buffer; + int32_t tx_size; + uint32_t handle; + uint8_t interface_index; +}; + +/** + * When a HID device is connected, the OS generates an attach + * event; the attach event handler translate them into these + * structures. + */ +struct wiiu_attach { + wiiu_attach_event *next; + hid_device_t *driver; + uint32_t type; + uint32_t handle; + uint16_t vendor_id; + uint16_t product_id; + uint8_t interface_index; + uint8_t is_keyboard; + uint8_t is_mouse; + uint16_t max_packet_size_rx; + uint16_t max_packet_size_tx; +}; + +struct _wiiu_event_list { + OSFastMutex lock; + wiiu_attach_event *list; +}; + +struct _wiiu_adapter_list { + OSFastMutex lock; + wiiu_adapter_t *list; +}; + +extern wiiu_pad_functions_t pad_functions; +extern input_device_driver_t wiiu_joypad; +extern input_device_driver_t wpad_driver; +extern input_device_driver_t kpad_driver; +extern input_device_driver_t hidpad_driver; +extern hid_driver_t wiiu_hid; static void *alloc_zeroed(size_t alignment, size_t size); static OSThread *new_thread(void); diff --git a/wiiu/input/wiiu_hid_types.h b/wiiu/input/wiiu_hid_types.h new file mode 100644 index 0000000000..59eaabaddf --- /dev/null +++ b/wiiu/input/wiiu_hid_types.h @@ -0,0 +1,28 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013-2014 - Jason Fetters + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __WIIU_HID_TYPES__H +#define __WIIU_HID_TYPES__H + +typedef struct wiiu_hid wiiu_hid_t; +typedef struct wiiu_adapter wiiu_adapter_t; +typedef struct wiiu_attach wiiu_attach_event; +typedef struct _wiiu_event_list wiiu_event_list; +typedef struct _wiiu_adapter_list wiiu_adapter_list; +typedef struct _axis_data axis_data; +typedef struct _wiiu_pad_functions wiiu_pad_functions_t; + +#endif /* __WIIU_HID_TYPES__H */ diff --git a/wiiu/input/wiiu_input.h b/wiiu/input/wiiu_input.h new file mode 100644 index 0000000000..16509b47f2 --- /dev/null +++ b/wiiu/input/wiiu_input.h @@ -0,0 +1,72 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013-2014 - Jason Fetters + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __WIIU_INPUT__H +#define __WIIU_INPUT__H + +#include "wiiu_hid_types.h" + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../input/input_driver.h" +#include "../../input/common/hid/hid_device_driver.h" +#include "../../tasks/tasks_internal.h" +#include "../../input/connect/joypad_connection.h" +#include "../../retroarch.h" +#include "../../verbosity.h" +#include "../../command.h" +#include "../../gfx/video_driver.h" +#include "wiiu_hid.h" + +#define WIIMOTE_TYPE_WIIPLUS 0x00 +#define WIIMOTE_TYPE_NUNCHUK 0x01 +#define WIIMOTE_TYPE_CLASSIC 0x02 +#define WIIMOTE_TYPE_PRO 0x1f +#define WIIMOTE_TYPE_NONE 0xfd + +#define WIIU_DEVICE_INDEX_TOUCHPAD 2 + +#define PAD_GAMEPAD 0 +#define WIIU_WIIMOTE_CHANNELS 4 + +#define WIIU_ANALOG_FACTOR 0x7ff0 +#define WIIU_READ_STICK(stick) ((stick) * WIIU_ANALOG_FACTOR) + +struct _axis_data { + int32_t axis; + bool is_negative; +}; + +struct _wiiu_pad_functions { + int16_t (*get_axis_value)(int32_t axis, int16_t state[3][2], bool is_negative); + void (*set_axis_value)(int16_t state[3][2], int16_t left_x, int16_t left_y, + int16_t right_x, int16_t right_y, int16_t touch_x, int16_t touch_y); + void (*read_axis_data)(uint32_t axis, axis_data *data); + void (*connect)(unsigned pad, input_device_driver_t *driver); +}; + +#endif /* __WIIU_INPUT__H */ diff --git a/wiiu/input/wpad_driver.c b/wiiu/input/wpad_driver.c index 786f883cda..5cac0c5bff 100644 --- a/wiiu/input/wpad_driver.c +++ b/wiiu/input/wpad_driver.c @@ -21,7 +21,9 @@ * - For HID controllers, see hid_driver.c */ -#include +#include "wiiu_input.h" + +#define PANIC_BUTTON_MASK (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L) static bool ready = false; static uint64_t button_state = 0; From 015facee70bdd4267ee4a85cc748cf9da3474b0d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 1 Apr 2018 18:23:37 +0200 Subject: [PATCH 104/517] (XMB) Cleanups --- menu/drivers/xmb.c | 129 +++++++++++++++++++++++++++------------------ 1 file changed, 79 insertions(+), 50 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 2418d79e0b..9e80415cba 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3025,41 +3025,42 @@ static void xmb_draw_dark_layer( static void xmb_frame(void *data, video_frame_info_t *video_info) { - size_t selection; - size_t percent_width = 0; math_matrix_4x4 mymat; unsigned i; - float thumb_width, thumb_height, left_thumb_width, left_thumb_height, thumb_max_width; menu_display_ctx_rotate_draw_t rotate_draw; char msg[1024]; char title_msg[255]; char title_truncated[255]; + size_t selection = 0; + size_t percent_width = 0; const int min_thumb_size = 50; - settings_t *settings = config_get_ptr(); - unsigned width = video_info->width; - unsigned height = video_info->height; bool render_background = false; file_list_t *selection_buf = NULL; - xmb_handle_t *xmb = (xmb_handle_t*)data; - float window_width = video_info->width; - float window_height = video_info->height; + unsigned width = video_info->width; + unsigned height = video_info->height; const float under_thumb_margin = 0.96; - float scale_factor = (settings->uints.menu_xmb_scale_factor * window_width) / (1920.0 * 100); - float pseudo_font_length = xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4; + float scale_factor = 0.0f; + float pseudo_font_length = 0.0f; + xmb_handle_t *xmb = (xmb_handle_t*)data; + settings_t *settings = config_get_ptr(); if (!xmb) return; + scale_factor = (settings->uints.menu_xmb_scale_factor * (float)width) / (1920.0 * 100); + pseudo_font_length = xmb->icon_spacing_horizontal + * 4 - xmb->icon_size / 4; + xmb->frame_count++; msg[0] = '\0'; title_msg[0] = '\0'; title_truncated[0] = '\0'; - font_driver_bind_block(xmb->font, &xmb->raster_block); + font_driver_bind_block(xmb->font, &xmb->raster_block); font_driver_bind_block(xmb->font2, &xmb->raster_block2); - xmb->raster_block.carr.coords.vertices = 0; + xmb->raster_block.carr.coords.vertices = 0; xmb->raster_block2.carr.coords.vertices = 0; menu_display_set_alpha(coord_black, MIN( @@ -3078,12 +3079,13 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) selection = menu_navigation_get_selection(); - strlcpy(title_truncated, xmb->title_name, sizeof(title_truncated)); + strlcpy(title_truncated, + xmb->title_name, sizeof(title_truncated)); if (selection > 1) { /* skip 25 utf8 multi-byte chars */ - char* end = title_truncated; + char *end = title_truncated; for(i = 0; i < 25 && *end; i++) { @@ -3120,8 +3122,10 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Do not draw the right thumbnail if there is no space available */ - if (((xmb->margins_screen_top + xmb->icon_size + min_thumb_size) <= height) && - ((xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + + if (((xmb->margins_screen_top + + xmb->icon_size + min_thumb_size) <= height) && + ((xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + pseudo_font_length + min_thumb_size) <= width)) { if (xmb->savestate_thumbnail) @@ -3129,23 +3133,29 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb, &coord_white[0], width, height, xmb->margins_screen_left * scale_mod[5] + xmb->icon_spacing_horizontal + pseudo_font_length, - xmb->margins_screen_top + xmb->icon_size + xmb->savestate_thumbnail_height * scale_mod[4], - xmb->savestate_thumbnail_width * scale_mod[4], xmb->savestate_thumbnail_height * scale_mod[4], + xmb->margins_screen_top + xmb->icon_size + + xmb->savestate_thumbnail_height * scale_mod[4], + xmb->savestate_thumbnail_width * scale_mod[4], + xmb->savestate_thumbnail_height * scale_mod[4], xmb->savestate_thumbnail); else if (xmb->thumbnail && !string_is_equal(xmb_thumbnails_ident('R'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { - #ifdef XMB_DEBUG - RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", xmb->thumbnail_width, xmb->thumbnail_height); - RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif /* Limit thumbnail width */ - - thumb_max_width = window_width - (xmb->icon_size / 6) - (xmb->margins_screen_left * scale_mod[5]) - + float thumb_width = 0.0f; + float thumb_height = 0.0f; + float thumb_max_width = (float)width - (xmb->icon_size / 6) + - (xmb->margins_screen_left * scale_mod[5]) - xmb->icon_spacing_horizontal - pseudo_font_length; +#ifdef XMB_DEBUG + RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", + xmb->thumbnail_width, xmb->thumbnail_height); + RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); +#endif + if (xmb->thumbnail_width * scale_mod[4] > thumb_max_width) { thumb_width = (xmb->thumbnail_width * scale_mod[4]) * @@ -3155,27 +3165,29 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } else { - thumb_width = xmb->thumbnail_width * scale_mod[4]; + thumb_width = xmb->thumbnail_width * scale_mod[4]; thumb_height = xmb->thumbnail_height * scale_mod[4]; } /* Limit thumbnail height to screen height + margin. */ if (xmb->margins_screen_top + xmb->icon_size + thumb_height >= - (window_height * under_thumb_margin)) + ((float)height * under_thumb_margin)) { thumb_width = thumb_width * - (((window_height * under_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / - thumb_height); + ((((float)height * under_thumb_margin) - + xmb->margins_screen_top - xmb->icon_size) / + thumb_height); thumb_height = thumb_height * - (((window_height * under_thumb_margin) - xmb->margins_screen_top - xmb->icon_size) / - thumb_height); + ((((float)height * under_thumb_margin) - + xmb->margins_screen_top - xmb->icon_size) / + thumb_height); } xmb_draw_thumbnail(video_info, xmb, &coord_white[0], width, height, - window_width - (xmb->icon_size / 6) - thumb_max_width + - ((thumb_max_width - thumb_width) / 2), + (float)width - (xmb->icon_size / 6) - thumb_max_width + + ((thumb_max_width - thumb_width) / 2), xmb->margins_screen_top + xmb->icon_size + thumb_height, thumb_width, thumb_height, xmb->thumbnail); @@ -3184,8 +3196,9 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Do not draw the left thumbnail if there is no space available */ - if ((xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) - <= window_height) + if ((xmb->margins_screen_top + xmb->icon_size * + (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) + <= (float)height) { /* Left Thumbnail */ @@ -3193,17 +3206,24 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) && !string_is_equal(xmb_thumbnails_ident('L'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { + float left_thumb_width = 0.0f; + float left_thumb_height = 0.0f; + /* Limit left thumbnail height to screen height + margin. */ - if (xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + - xmb->left_thumbnail_height >= (window_height - (96.0 * scale_factor))) + if (xmb->margins_screen_top + xmb->icon_size * + (!(xmb->depth == 1)? 2.1 : 1) + + xmb->left_thumbnail_height >= + ((float)height - (96.0 * scale_factor))) { left_thumb_width = xmb->left_thumbnail_width * - (((window_height - (96.0 * scale_factor)) - xmb->margins_screen_top - + ((((float)height - (96.0 * scale_factor)) + - xmb->margins_screen_top - (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / xmb->left_thumbnail_height); left_thumb_height = xmb->left_thumbnail_height * - (((window_height - (96.0 * scale_factor)) - xmb->margins_screen_top - + ((((float)height - (96.0 * scale_factor)) + - xmb->margins_screen_top - (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / xmb->left_thumbnail_height); } @@ -3215,13 +3235,15 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb_draw_thumbnail(video_info, xmb, &coord_white[0], width, height, - (xmb->icon_size / 6) + ((xmb->left_thumbnail_width - left_thumb_width) / 2), + (xmb->icon_size / 6) + + ((xmb->left_thumbnail_width - left_thumb_width) / 2), xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + left_thumb_height, left_thumb_width, left_thumb_height, xmb->left_thumbnail); } } + /* Clock image */ menu_display_set_alpha(coord_white, MIN(xmb->alpha, 1.00f)); @@ -3268,7 +3290,9 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) snprintf(msg, sizeof(msg), "%d%%", percent); - percent_width = (unsigned)font_driver_get_message_width(xmb->font, msg, (unsigned)strlen(msg), 1); + percent_width = (unsigned) + font_driver_get_message_width( + xmb->font, msg, (unsigned)strlen(msg), 1); xmb_draw_text(video_info, xmb, msg, width - xmb->margins_title_left - x_pos, @@ -3323,7 +3347,8 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } /* Arrow image */ - menu_display_set_alpha(coord_white, MIN(xmb->textures_arrow_alpha, xmb->alpha)); + menu_display_set_alpha(coord_white, + MIN(xmb->textures_arrow_alpha, xmb->alpha)); if (coord_white[3] != 0) xmb_draw_icon(video_info, @@ -3331,7 +3356,8 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) &mymat, xmb->textures.list[XMB_TEXTURE_ARROW], xmb->x + xmb->margins_screen_left + - xmb->icon_spacing_horizontal - xmb->icon_size / 2.0 + xmb->icon_size, + xmb->icon_spacing_horizontal - + xmb->icon_size / 2.0 + xmb->icon_size, xmb->margins_screen_top + xmb->icon_size / 2.0 + xmb->icon_spacing_vertical * xmb->active_item_factor, @@ -3363,8 +3389,10 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) uintptr_t texture = node->icon; float x = xmb->x + xmb->categories_x_pos + xmb->margins_screen_left + - xmb->icon_spacing_horizontal * (i + 1) - xmb->icon_size / 2.0; - float y = xmb->margins_screen_top + xmb->icon_size / 2.0; + xmb->icon_spacing_horizontal + * (i + 1) - xmb->icon_size / 2.0; + float y = xmb->margins_screen_top + + xmb->icon_size / 2.0; float rotation = 0; float scale_factor = node->zoom; @@ -3403,7 +3431,8 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb->selection_buf_old, xmb->selection_ptr_old, (xmb_list_get_size(xmb, MENU_LIST_PLAIN) > 1) - ? xmb->categories_selection_ptr : xmb->categories_selection_ptr_old, + ? xmb->categories_selection_ptr : + xmb->categories_selection_ptr_old, &item_color[0], width, height); @@ -3551,9 +3580,9 @@ static void xmb_layout_psp(xmb_handle_t *xmb, int width) settings_t *settings = config_get_ptr(); float scale_factor = ((settings->uints.menu_xmb_scale_factor * width) / (1920.0 * 100)) * 1.5; - #ifdef _3DS - scale_factor = settings->uints.menu_xmb_scale_factor / 400.0; + scale_factor = + settings->uints.menu_xmb_scale_factor / 400.0; #endif xmb->above_subitem_offset = 1.5; @@ -3634,8 +3663,8 @@ static void xmb_layout(xmb_handle_t *xmb) for (i = 0; i < end; i++) { - float ia = xmb->items_passive_alpha; - float iz = xmb->items_passive_zoom; + float ia = xmb->items_passive_alpha; + float iz = xmb->items_passive_zoom; xmb_node_t *node = (xmb_node_t*)file_list_get_userdata_at_offset( selection_buf, i); From 78c8a9b197df4cdb9ad58b0878077f914de1459f Mon Sep 17 00:00:00 2001 From: Dwedit Date: Sun, 1 Apr 2018 17:34:54 -0500 Subject: [PATCH 105/517] Make RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE work on the secondary core --- runahead/run_ahead.c | 17 ++++++++++++++++- runahead/secondary_core.c | 32 +++++++++++++++++++++++++++++++- runahead/secondary_core.h | 1 + 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index f90736abeb..1e931f5265 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -112,6 +112,7 @@ static void remove_hooks(void) current_core.retro_unload_game = originalRetroUnload; originalRetroUnload = NULL; } + current_core.retro_set_environment(rarch_environment_cb); remove_input_state_hook(); } @@ -134,6 +135,20 @@ static void deinit_hook(void) current_core.retro_deinit(); } +static bool env_hook(unsigned cmd, void *data) +{ + bool result = rarch_environment_cb(cmd, data); + if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE && result) + { + bool *bool_p = (bool*)data; + if (*bool_p == true) + { + secondary_core_set_variable_update(); + } + } + return result; +} + static void add_hooks(void) { if (!originalRetroDeinit) @@ -147,7 +162,7 @@ static void add_hooks(void) originalRetroUnload = current_core.retro_unload_game; current_core.retro_unload_game = unload_hook; } - + current_core.retro_set_environment(env_hook); add_input_state_hook(); } diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 9aba8323ba..1dbc571d97 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -22,6 +22,8 @@ #include "../paths.h" #include "../content.h" +#include "secondary_core.h" + static int port_map[16]; static char *secondary_library_path; @@ -46,6 +48,8 @@ bool secondary_core_run_no_input_polling(void); bool secondary_core_deserialize(const void *buffer, int size); +static bool rarch_environment_secondary_core_hook(unsigned cmd, void *data); + void secondary_core_destroy(void); void set_last_core_type(enum rarch_core_type type); @@ -205,7 +209,8 @@ bool secondary_core_create(void) secondary_core.retro_set_audio_sample_batch(secondary_callbacks.sample_batch_cb); secondary_core.retro_set_input_state(secondary_callbacks.state_cb); secondary_core.retro_set_input_poll(secondary_callbacks.poll_cb); - secondary_core.retro_set_environment(rarch_environment_cb); + secondary_core.retro_set_environment(rarch_environment_secondary_core_hook); + secondary_core_set_variable_update(); secondary_core.retro_init(); @@ -269,6 +274,26 @@ bool secondary_core_create(void) return true; } +static bool has_variable_update; + +static bool rarch_environment_secondary_core_hook(unsigned cmd, void *data) +{ + bool result = rarch_environment_cb(cmd, data); + if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE && has_variable_update) + { + has_variable_update = false; + bool *bool_p = (bool*)data; + *bool_p = true; + return true; + } + return result; +} + +void secondary_core_set_variable_update(void) +{ + has_variable_update = true; +} + bool secondary_core_run_no_input_polling(void) { if (!secondary_module) @@ -350,5 +375,10 @@ void remember_controller_port_device(long port, long device) { /* do nothing */ } +void secondary_core_set_variable_update(void) +{ + /* do nothing */ +} + #endif diff --git a/runahead/secondary_core.h b/runahead/secondary_core.h index b135aee81b..324c882c6d 100644 --- a/runahead/secondary_core.h +++ b/runahead/secondary_core.h @@ -14,6 +14,7 @@ void secondary_core_destroy(void); void set_last_core_type(enum rarch_core_type type); void remember_controller_port_device(long port, long device); void clear_controller_port_map(void); +void secondary_core_set_variable_update(void); RETRO_END_DECLS From 64686d810603936fab9fc894a998248e0ff69c85 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Sun, 1 Apr 2018 14:52:29 -0500 Subject: [PATCH 106/517] Set override-redirect on true fullscreen, but after window is mapped. --- gfx/common/x11_common.c | 41 ------------------------------------- gfx/common/x11_common.h | 1 - gfx/drivers_context/x_ctx.c | 10 ++++----- 3 files changed, 5 insertions(+), 47 deletions(-) diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index b1eb6d2886..57133f344f 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -107,47 +107,6 @@ void x11_show_mouse(Display *dpy, Window win, bool state) x11_hide_mouse(dpy, win); } -static bool x11_check_atom_supported(Display *dpy, Atom atom) -{ - Atom XA_NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", True); - Atom type; - int format; - unsigned long nitems; - unsigned long bytes_after; - Atom *prop; - int i; - - if (XA_NET_SUPPORTED == None) - return false; - - XGetWindowProperty(dpy, DefaultRootWindow(dpy), XA_NET_SUPPORTED, 0, UINT_MAX, False, XA_ATOM, &type, &format,&nitems, &bytes_after, (unsigned char **) &prop); - - if (!prop || type != XA_ATOM) - { - return false; - } - - for (i = 0; i < nitems; i++) - { - if (prop[i] == atom) - { - XFree(prop); - return true; - } - } - - XFree(prop); - - return false; -} - -bool x11_has_net_wm_fullscreen(Display *dpy) -{ - XA_NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); - - return x11_check_atom_supported(dpy, XA_NET_WM_STATE_FULLSCREEN); -} - void x11_set_net_wm_fullscreen(Display *dpy, Window win) { XEvent xev = {0}; diff --git a/gfx/common/x11_common.h b/gfx/common/x11_common.h index 54718b8ce8..20d8d49080 100644 --- a/gfx/common/x11_common.h +++ b/gfx/common/x11_common.h @@ -29,7 +29,6 @@ extern Colormap g_x11_cmap; extern unsigned g_x11_screen; void x11_show_mouse(Display *dpy, Window win, bool state); -bool x11_has_net_wm_fullscreen(Display *dpy); void x11_set_net_wm_fullscreen(Display *dpy, Window win); void x11_suspend_screensaver(Window win, bool enable); bool x11_enter_fullscreen(video_frame_info_t *video_info, diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index eec1f3dc2b..07bf73d5e5 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -691,7 +691,7 @@ static bool gfx_ctx_x_set_video_mode(void *data, RARCH_ERR("[GLX]: Entering true fullscreen failed. Will attempt windowed mode.\n"); } - swa.override_redirect = (!x11_has_net_wm_fullscreen(g_x11_dpy) && true_full) ? True : False; + swa.override_redirect = (true_full) ? True : False; if (video_info->monitor_index) g_x11_screen = video_info->monitor_index - 1; @@ -722,8 +722,8 @@ static bool gfx_ctx_x_set_video_mode(void *data, g_x11_win = XCreateWindow(g_x11_dpy, RootWindow(g_x11_dpy, vi->screen), x_off, y_off, width, height, 0, vi->depth, InputOutput, vi->visual, - CWBorderPixel | CWColormap | CWEventMask | - CWOverrideRedirect, &swa); + CWBorderPixel | CWColormap | CWEventMask, + &swa); XSetWindowBackground(g_x11_dpy, g_x11_win, 0); XChangeProperty(g_x11_dpy, g_x11_win, net_wm_icon, cardinal, 32, PropModeReplace, (const unsigned char*)retroarch_icon_data, sizeof(retroarch_icon_data) / sizeof(*retroarch_icon_data)); @@ -780,8 +780,8 @@ static bool gfx_ctx_x_set_video_mode(void *data, { RARCH_LOG("[GLX]: Using true fullscreen.\n"); XMapRaised(g_x11_dpy, g_x11_win); - if (swa.override_redirect == False) - x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); + x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); + XChangeWindowAttributes(g_x11_dpy, g_x11_win, CWOverrideRedirect, &swa); } else if (fullscreen) { From 8356300c3e9989d1318373224c7f43b6471ab119 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Sun, 1 Apr 2018 17:44:04 -0500 Subject: [PATCH 107/517] Apply the same fix to xegl_ctx.c --- gfx/drivers_context/x_ctx.c | 2 +- gfx/drivers_context/xegl_ctx.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index 07bf73d5e5..2940b50989 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -691,7 +691,7 @@ static bool gfx_ctx_x_set_video_mode(void *data, RARCH_ERR("[GLX]: Entering true fullscreen failed. Will attempt windowed mode.\n"); } - swa.override_redirect = (true_full) ? True : False; + swa.override_redirect = true_full ? True : False; if (video_info->monitor_index) g_x11_screen = video_info->monitor_index - 1; diff --git a/gfx/drivers_context/xegl_ctx.c b/gfx/drivers_context/xegl_ctx.c index b470ccc6db..4338545999 100644 --- a/gfx/drivers_context/xegl_ctx.c +++ b/gfx/drivers_context/xegl_ctx.c @@ -310,7 +310,7 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, RARCH_ERR("[X/EGL]: Entering true fullscreen failed. Will attempt windowed mode.\n"); } - swa.override_redirect = (!x11_has_net_wm_fullscreen(g_x11_dpy) && true_full) ? True : False; + swa.override_redirect = true_full ? True : False; if (video_info->monitor_index) g_x11_screen = video_info->monitor_index - 1; @@ -341,8 +341,8 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, g_x11_win = XCreateWindow(g_x11_dpy, RootWindow(g_x11_dpy, vi->screen), x_off, y_off, width, height, 0, vi->depth, InputOutput, vi->visual, - CWBorderPixel | CWColormap | CWEventMask | - CWOverrideRedirect, &swa); + CWBorderPixel | CWColormap | CWEventMask, + &swa); XSetWindowBackground(g_x11_dpy, g_x11_win, 0); if (fullscreen && settings && settings->bools.video_disable_composition) @@ -374,8 +374,8 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, { RARCH_LOG("[X/EGL]: Using true fullscreen.\n"); XMapRaised(g_x11_dpy, g_x11_win); - if (swa.override_redirect == False) - x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); + x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); + XChangeWindowAttributes(g_x11_dpy, g_x11_win, CWOverrideRedirect, &swa); } else if (fullscreen) { From a275242aefeddc18e33e3a21a73ee2b0eef427b2 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Sun, 1 Apr 2018 17:54:03 -0500 Subject: [PATCH 108/517] C89 compliance and line spacing rules --- runahead/run_ahead.c | 2 -- runahead/secondary_core.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 1e931f5265..a3a1cf5a87 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -142,9 +142,7 @@ static bool env_hook(unsigned cmd, void *data) { bool *bool_p = (bool*)data; if (*bool_p == true) - { secondary_core_set_variable_update(); - } } return result; } diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 1dbc571d97..5be3a0f9b2 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -281,9 +281,9 @@ static bool rarch_environment_secondary_core_hook(unsigned cmd, void *data) bool result = rarch_environment_cb(cmd, data); if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE && has_variable_update) { - has_variable_update = false; bool *bool_p = (bool*)data; *bool_p = true; + has_variable_update = false; return true; } return result; From 9bc5a15c2de83e40ac339685906b9e742ae2b7f0 Mon Sep 17 00:00:00 2001 From: gblues Date: Sun, 1 Apr 2018 18:52:26 -0700 Subject: [PATCH 109/517] Enable pads to register in any order == DETAILS Whereas the last commit had a hack (that disabled the wiimote driver in the process), this has.. well, a *different* hack that allows pads to register in any order. Note that due to the initialization routines, the gamepad will still likely always get slot 0. Not sure if this can be overridden via config or not. == TESTING Tested locally with GC adapter --- input/common/hid/hid_device_driver.c | 23 ++++++-- input/drivers_joypad/wiiu_joypad.c | 79 +++++++++++----------------- input/include/gamepad.h | 27 ++++++++++ input/include/hid_driver.h | 4 +- input/input_driver.c | 18 +++++++ input/input_driver.h | 23 ++++---- input/input_types.h | 26 +++++++++ wiiu/input/hidpad_driver.c | 10 ---- wiiu/input/kpad_driver.c | 68 ++++++++++++++++++------ wiiu/input/wpad_driver.c | 9 +++- 10 files changed, 193 insertions(+), 94 deletions(-) create mode 100644 input/include/gamepad.h create mode 100644 input/input_types.h diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c index 3792e2c737..ee0ef38a58 100644 --- a/input/common/hid/hid_device_driver.c +++ b/input/common/hid/hid_device_driver.c @@ -57,6 +57,24 @@ joypad_connection_t *hid_pad_register(void *pad_handle, pad_connection_interface return result; } +static bool init_pad_list(hid_driver_instance_t *instance, unsigned slots) +{ + if(!instance || slots > MAX_USERS) + return false; + + if(instance->pad_list) + return true; + + RARCH_LOG("[hid]: initializing pad list...\n"); + instance->pad_list = pad_connection_init(slots); + if(!instance->pad_list) + return false; + + instance->max_slot = slots; + + return true; +} + /** * Fill in instance with data from initialized hid subsystem. * @@ -80,16 +98,13 @@ bool hid_init(hid_driver_instance_t *instance, if(!instance->os_driver_data) return false; - RARCH_LOG("[hid]: initializing pad list...\n"); - instance->pad_list = pad_connection_init(slots); - if(!instance->pad_list) + if(!init_pad_list(instance, slots)) { hid_driver->free(instance->os_driver_data); instance->os_driver_data = NULL; return false; } - instance->max_slot = slots; instance->os_driver = hid_driver; instance->pad_driver = pad_driver; diff --git a/input/drivers_joypad/wiiu_joypad.c b/input/drivers_joypad/wiiu_joypad.c index dc0ab8fe94..b4725a9d4d 100644 --- a/input/drivers_joypad/wiiu_joypad.c +++ b/input/drivers_joypad/wiiu_joypad.c @@ -19,6 +19,8 @@ #include "wiiu_dbg.h" static input_device_driver_t *pad_drivers[MAX_USERS]; +extern pad_connection_listener_t wiiu_pad_connection_listener; + static bool ready = false; @@ -31,61 +33,30 @@ static int16_t wiiu_joypad_axis(unsigned pad, uint32_t axis); static void wiiu_joypad_poll(void); static const char *wiiu_joypad_name(unsigned pad); -/** - * Translates a pad to its appropriate driver. - * Note that this is a helper for build_pad_map and shouldn't be - * used directly. - */ -static input_device_driver_t *get_driver_for_pad(unsigned pad) -{ - if(wpad_driver.query_pad(pad)) - return &wpad_driver; - - if(kpad_driver.query_pad(pad)) - return &kpad_driver; - -#ifdef WIIU_HID - return &hidpad_driver; -#else - return NULL; -#endif -} - -/** - * Populates the pad_driver array. We do this once at init time so - * that lookups at runtime are constant time. - */ -static void build_pad_map(void) -{ - unsigned i; - - for(i = 0; i < MAX_USERS; i++) - { - pad_drivers[i] = get_driver_for_pad(i); - } -} - static bool wiiu_joypad_init(void* data) { - /* the sub-drivers have to init first, otherwise - * build_pad_map will fail (because all lookups will return false). */ - wpad_driver.init(data); - kpad_driver.init(data); + set_connection_listener(&wiiu_pad_connection_listener); + hid_instance.pad_list = pad_connection_init(MAX_USERS); + hid_instance.max_slot = MAX_USERS; + + wpad_driver.init(data); + kpad_driver.init(data); #ifdef WIIU_HID - hidpad_driver.init(data); + hidpad_driver.init(data); #endif - build_pad_map(); + ready = true; + (void)data; - ready = true; - (void)data; - - return true; + return true; } static bool wiiu_joypad_query_pad(unsigned pad) { - return ready && pad < MAX_USERS; + return ready && + pad < MAX_USERS && + pad_drivers[pad] != NULL && + pad_drivers[pad]->query_pad(pad); } static void wiiu_joypad_destroy(void) @@ -134,10 +105,17 @@ static void wiiu_joypad_poll(void) static const char* wiiu_joypad_name(unsigned pad) { - if(!wiiu_joypad_query_pad(pad)) - return "N/A"; + if(!wiiu_joypad_query_pad(pad)) + return "N/A"; - return pad_drivers[pad]->name(pad); + return pad_drivers[pad]->name(pad); +} + +static void wiiu_joypad_connection_listener(unsigned pad, + input_device_driver_t *driver) +{ + if(pad < MAX_USERS) + pad_drivers[pad] = driver; } input_device_driver_t wiiu_joypad = @@ -153,3 +131,8 @@ input_device_driver_t wiiu_joypad = wiiu_joypad_name, "wiiu", }; + +pad_connection_listener_t wiiu_pad_connection_listener = +{ + wiiu_joypad_connection_listener +}; diff --git a/input/include/gamepad.h b/input/include/gamepad.h new file mode 100644 index 0000000000..8acb9027d1 --- /dev/null +++ b/input/include/gamepad.h @@ -0,0 +1,27 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2017 - Andrés Suárez + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef GAMEPAD_H__ +#define GAMEPAD_H__ + +#include "../input_driver.h" + +typedef struct pad_connection_listener_interface { + void (*connected)(unsigned port, input_device_driver_t *driver); +} pad_connection_listener_t; + +#endif /* GAMEPAD_H__ */ diff --git a/input/include/hid_driver.h b/input/include/hid_driver.h index 619f491688..654c3849ea 100644 --- a/input/include/hid_driver.h +++ b/input/include/hid_driver.h @@ -58,8 +58,8 @@ struct hid_driver hid_instance.os_driver_data, pad, key) #define HID_AXIS(pad, axis) hid_instance.os_driver->axis( \ hid_instance.os_driver_data, pad, axis) -#define HID_PAD_NAME(pad) hid_instance.os_driver->name( \ - hid_instance.os_driver_data, pad) +#define HID_PAD_NAME(pad) \ + hid_instance.os_driver->name(hid_instance.os_driver_data, pad) #define HID_POLL() hid_instance.os_driver->poll( \ hid_instance.os_driver_data) #define HID_MAX_SLOT() hid_instance.max_slot diff --git a/input/input_driver.c b/input/input_driver.c index 882d33ca1b..78d50590d1 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -60,6 +60,22 @@ #include "../verbosity.h" #include "../tasks/tasks_internal.h" #include "../command.h" +#include "include/gamepad.h" + +static pad_connection_listener_t *pad_connection_listener = NULL; + +void set_connection_listener(pad_connection_listener_t *listener) +{ + pad_connection_listener = listener; +} + +void fire_connection_listener(unsigned port, input_device_driver_t *driver) +{ + if(!pad_connection_listener) + return; + + pad_connection_listener->connected(port, driver); +} static const input_driver_t *input_drivers[] = { #ifdef __CELLOS_LV2__ @@ -1790,6 +1806,8 @@ void input_pad_connect(unsigned port, input_device_driver_t *driver) return; } + fire_connection_listener(port, driver); + if(!input_autoconfigure_connect(driver->name(port), NULL, driver->ident, port, 0, 0)) input_config_set_device_name(port, driver->name(port)); diff --git a/input/input_driver.h b/input/input_driver.h index b366413893..67a42fecc8 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -22,6 +22,8 @@ #include #include +#include "input_types.h" + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -36,14 +38,10 @@ #include "../msg_hash.h" #include "include/hid_types.h" +#include "include/gamepad.h" RETRO_BEGIN_DECLS -typedef struct rarch_joypad_driver input_device_driver_t; - -/* Keyboard line reader. Handles textual input in a direct fashion. */ -typedef struct input_keyboard_line input_keyboard_line_t; - enum input_device_type { INPUT_DEVICE_TYPE_NONE = 0, @@ -120,14 +118,14 @@ struct retro_keybind char *joyaxis_label; }; -typedef struct rarch_joypad_info +struct rarch_joypad_info { uint16_t joy_idx; const struct retro_keybind *auto_binds; float axis_threshold; -} rarch_joypad_info_t; +}; -typedef struct input_driver +struct input_driver { /* Inits input driver. */ @@ -163,7 +161,7 @@ typedef struct input_driver const input_device_driver_t *(*get_sec_joypad_driver)(void *data); bool (*keyboard_mapping_is_blocked)(void *data); void (*keyboard_mapping_set_block)(void *data, bool value); -} input_driver_t; +}; struct rarch_joypad_driver { @@ -657,11 +655,11 @@ typedef void (*input_keyboard_line_complete_t)(void *userdata, typedef bool (*input_keyboard_press_t)(void *userdata, unsigned code); -typedef struct input_keyboard_ctx_wait +struct input_keyboard_ctx_wait { void *userdata; input_keyboard_press_t cb; -} input_keyboard_ctx_wait_t; +}; /** * input_keyboard_event: @@ -789,6 +787,9 @@ uint16_t input_config_get_vid(unsigned port); void input_config_reset(void); +void set_connection_listener(pad_connection_listener_t *listener); +void fire_connection_listener(unsigned port, input_device_driver_t *driver); + extern input_device_driver_t dinput_joypad; extern input_device_driver_t linuxraw_joypad; extern input_device_driver_t parport_joypad; diff --git a/input/input_types.h b/input/input_types.h new file mode 100644 index 0000000000..4c36861866 --- /dev/null +++ b/input/input_types.h @@ -0,0 +1,26 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __INPUT_TYPES__H +#define __INPUT_TYPES__H + +typedef struct rarch_joypad_driver input_device_driver_t; +typedef struct input_keyboard_line input_keyboard_line_t; +typedef struct rarch_joypad_info rarch_joypad_info_t; +typedef struct input_driver input_driver_t; +typedef struct input_keyboard_ctx_wait input_keyboard_ctx_wait_t; + +#endif /* __INPUT_TYPES__H */ diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c index 896ba4b2d4..8b1f1adeae 100644 --- a/wiiu/input/hidpad_driver.c +++ b/wiiu/input/hidpad_driver.c @@ -30,8 +30,6 @@ static bool ready = false; static bool init_hid_driver(void) { - memset(&hid_instance, 0, sizeof(hid_instance)); - return hid_init(&hid_instance, &wiiu_hid, &hidpad_driver, MAX_USERS); } @@ -46,14 +44,6 @@ static bool hidpad_init(void *data) return false; } - hid_instance.pad_list[0].connected = true; -/* - for(i = 0; i < (WIIU_WIIMOTE_CHANNELS+1); i++) - { - hid_instance.pad_list[i].connected = true; - } -*/ - hidpad_poll(); ready = true; diff --git a/wiiu/input/kpad_driver.c b/wiiu/input/kpad_driver.c index faebe1143e..006a180d97 100644 --- a/wiiu/input/kpad_driver.c +++ b/wiiu/input/kpad_driver.c @@ -52,17 +52,30 @@ wiimote_state wiimotes[WIIU_WIIMOTE_CHANNELS] = { { 0, {{0,0},{0,0},{0,0}}, WIIMOTE_TYPE_NONE }, }; -static unsigned to_wiimote_channel(unsigned pad) -{ - if (pad == PAD_GAMEPAD || pad > WIIU_WIIMOTE_CHANNELS) - return 0xffffffff; +static int channel_slot_map[] = { -1, -1, -1, -1 }; - return pad-1; +static int to_wiimote_channel(unsigned pad) +{ + int i; + + for(i = 0; i < WIIU_WIIMOTE_CHANNELS; i++) + if(channel_slot_map[i] == pad) + return i; + + return -1; } -static unsigned to_retro_pad(unsigned channel) +static int get_slot_for_channel(unsigned channel) { - return channel+1; + int slot = pad_connection_find_vacant_pad(hid_instance.pad_list); + if(slot >= 0) + { + RARCH_LOG("[kpad]: got slot %d\n", slot); + channel_slot_map[channel] = slot; + hid_instance.pad_list[slot].connected = true; + } + + return slot; } static bool kpad_init(void *data) @@ -75,7 +88,7 @@ static bool kpad_init(void *data) static bool kpad_query_pad(unsigned pad) { - return ready && pad <= WIIU_WIIMOTE_CHANNELS && pad > PAD_GAMEPAD; + return ready && pad < MAX_USERS; } static void kpad_destroy(void) @@ -88,27 +101,34 @@ static bool kpad_button(unsigned pad, uint16_t button_bit) if (!kpad_query_pad(pad)) return false; - return wiimotes[to_wiimote_channel(pad)].button_state + int channel = to_wiimote_channel(pad); + if(channel < 0) + return false; + + return wiimotes[channel].button_state & (UINT64_C(1) << button_bit); } static void kpad_get_buttons(unsigned pad, retro_bits_t *state) { - if (!kpad_query_pad(pad)) + int channel = to_wiimote_channel(pad); + + if (!kpad_query_pad(pad) || channel < 0) BIT256_CLEAR_ALL_PTR(state); else - BITS_COPY16_PTR(state, wiimotes[to_wiimote_channel(pad)].button_state); + BITS_COPY16_PTR(state, wiimotes[channel].button_state); } static int16_t kpad_axis(unsigned pad, uint32_t axis) { + int channel = to_wiimote_channel(pad); axis_data data; - if (!kpad_query_pad(pad) || axis == AXIS_NONE) + if (!kpad_query_pad(pad) || channel < 0 || axis == AXIS_NONE) return 0; pad_functions.read_axis_data(axis, &data); return pad_functions.get_axis_value(data.axis, - wiimotes[to_wiimote_channel(pad)].analog_state, + wiimotes[channel].analog_state, data.is_negative); } @@ -116,8 +136,15 @@ static void kpad_register(unsigned channel, uint8_t device_type) { if (wiimotes[channel].type != device_type) { + int slot = get_slot_for_channel(channel); + if(slot < 0) + { + RARCH_ERR("Couldn't get a slot for this remote.\n"); + return; + } + wiimotes[channel].type = device_type; - input_pad_connect(to_retro_pad(channel), &kpad_driver); + input_pad_connect(slot, &kpad_driver); } } @@ -173,6 +200,13 @@ static void kpad_poll(void) result = KPADRead(channel, &kpad, 1); if (result == 0) { + int slot = channel_slot_map[channel]; + + if(slot > 0) + { + hid_instance.pad_list[slot].connected = false; + channel_slot_map[channel] = -1; + } continue; } @@ -182,11 +216,11 @@ static void kpad_poll(void) static const char *kpad_name(unsigned pad) { - pad = to_wiimote_channel(pad); - if (pad >= WIIU_WIIMOTE_CHANNELS) + int channel = to_wiimote_channel(pad); + if (channel < 0) return "unknown"; - switch(wiimotes[pad].type) + switch(wiimotes[channel].type) { case WIIMOTE_TYPE_PRO: return PAD_NAME_WIIU_PRO; diff --git a/wiiu/input/wpad_driver.c b/wiiu/input/wpad_driver.c index 5cac0c5bff..c66fb066f2 100644 --- a/wiiu/input/wpad_driver.c +++ b/wiiu/input/wpad_driver.c @@ -186,7 +186,12 @@ static void wpad_poll(void) static bool wpad_init(void *data) { - input_pad_connect(PAD_GAMEPAD, &wpad_driver); + int slot = pad_connection_find_vacant_pad(hid_instance.pad_list); + if(slot < 0) + return false; + + hid_instance.pad_list[slot].connected = true; + input_pad_connect(slot, &wpad_driver); wpad_poll(); ready = true; @@ -195,7 +200,7 @@ static bool wpad_init(void *data) static bool wpad_query_pad(unsigned pad) { - return ready && pad == PAD_GAMEPAD; + return ready && pad < MAX_USERS; } static void wpad_destroy(void) From 0a5e65dc06f5bb07f2d02974727d9df0a101032a Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Mon, 2 Apr 2018 20:01:14 -0500 Subject: [PATCH 110/517] Add workarounds based on window manager for override-redirect. --- gfx/common/x11_common.c | 62 ++++++++++++++++++++++++++++++++++ gfx/common/x11_common.h | 2 ++ gfx/drivers_context/x_ctx.c | 18 ++++++++-- gfx/drivers_context/xegl_ctx.c | 18 ++++++++-- 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index 57133f344f..1ab16b90b7 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -685,3 +685,65 @@ void x11_event_queue_check(XEvent *event) XIfEvent(g_x11_dpy, event, x11_wait_notify, NULL); } +char *x11_get_wm_name(Display *dpy) +{ + Atom XA_NET_SUPPORTING_WM_CHECK = XInternAtom(g_x11_dpy, "_NET_SUPPORTING_WM_CHECK", False); + Atom XA_NET_WM_NAME = XInternAtom(g_x11_dpy, "_NET_WM_NAME", False); + Atom XA_UTF8_STRING = XInternAtom(g_x11_dpy, "UTF8_STRING", False); + int status; + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + unsigned char *propdata; + char *title; + Window window; + + if (!XA_NET_SUPPORTING_WM_CHECK || !XA_NET_WM_NAME) + return NULL; + + status = XGetWindowProperty(dpy, + DefaultRootWindow(dpy), + XA_NET_SUPPORTING_WM_CHECK, + 0, + 1, + False, + XA_WINDOW, + &type, + &format, + &nitems, + &bytes_after, + &propdata); + + if (status == Success && propdata) + window = ((Window *) propdata)[0]; + else + return NULL; + + XFree(propdata); + + status = XGetWindowProperty(dpy, + window, + XA_NET_WM_NAME, + 0, + 8192, + False, + XA_UTF8_STRING, + &type, + &format, + &nitems, + &bytes_after, + &propdata); + + if (status == Success && propdata) + { + title = strdup((char *) propdata); + } + else + return NULL; + + XFree(propdata); + + return title; +} + diff --git a/gfx/common/x11_common.h b/gfx/common/x11_common.h index 20d8d49080..ec4fa0dc28 100644 --- a/gfx/common/x11_common.h +++ b/gfx/common/x11_common.h @@ -75,5 +75,7 @@ void x11_install_quit_atom(void); void x11_event_queue_check(XEvent *event); +char *x11_get_wm_name(Display *dpy); + #endif diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index 2940b50989..387336f6a9 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -632,6 +632,7 @@ static bool gfx_ctx_x_set_video_mode(void *data, int y_off = 0; XVisualInfo *vi = NULL; XSetWindowAttributes swa = {0}; + char *wm_name = NULL; int (*old_handler)(Display*, XErrorEvent*) = NULL; gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data; Atom net_wm_icon = XInternAtom(g_x11_dpy, "_NET_WM_ICON", False); @@ -679,6 +680,7 @@ static bool gfx_ctx_x_set_video_mode(void *data, swa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | LeaveWindowMask | EnterWindowMask | ButtonReleaseMask | ButtonPressMask; + swa.override_redirect = False; if (fullscreen && !windowed_full) { @@ -691,7 +693,18 @@ static bool gfx_ctx_x_set_video_mode(void *data, RARCH_ERR("[GLX]: Entering true fullscreen failed. Will attempt windowed mode.\n"); } - swa.override_redirect = true_full ? True : False; + wm_name = x11_get_wm_name(g_x11_dpy); + if (wm_name) + { + RARCH_LOG("[GLX]: Window manager is %s.\n", wm_name); + + if (true_full && strcasestr(wm_name, "xfwm")) + { + RARCH_LOG("[GLX]: Using override-redirect workaround.\n"); + swa.override_redirect = True; + } + free(wm_name); + } if (video_info->monitor_index) g_x11_screen = video_info->monitor_index - 1; @@ -722,7 +735,7 @@ static bool gfx_ctx_x_set_video_mode(void *data, g_x11_win = XCreateWindow(g_x11_dpy, RootWindow(g_x11_dpy, vi->screen), x_off, y_off, width, height, 0, vi->depth, InputOutput, vi->visual, - CWBorderPixel | CWColormap | CWEventMask, + CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &swa); XSetWindowBackground(g_x11_dpy, g_x11_win, 0); @@ -781,7 +794,6 @@ static bool gfx_ctx_x_set_video_mode(void *data, RARCH_LOG("[GLX]: Using true fullscreen.\n"); XMapRaised(g_x11_dpy, g_x11_win); x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); - XChangeWindowAttributes(g_x11_dpy, g_x11_win, CWOverrideRedirect, &swa); } else if (fullscreen) { diff --git a/gfx/drivers_context/xegl_ctx.c b/gfx/drivers_context/xegl_ctx.c index 4338545999..2f120ad956 100644 --- a/gfx/drivers_context/xegl_ctx.c +++ b/gfx/drivers_context/xegl_ctx.c @@ -272,6 +272,7 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, XVisualInfo temp = {0}; XSetWindowAttributes swa = {0}; XVisualInfo *vi = NULL; + char *wm_name = NULL; xegl_ctx_data_t *xegl = (xegl_ctx_data_t*)data; settings_t *settings = config_get_ptr(); @@ -298,6 +299,7 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, vi->visual, AllocNone); swa.event_mask = StructureNotifyMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | KeyReleaseMask; + swa.override_redirect = False; if (fullscreen && !video_info->windowed_fullscreen) { @@ -310,7 +312,18 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, RARCH_ERR("[X/EGL]: Entering true fullscreen failed. Will attempt windowed mode.\n"); } - swa.override_redirect = true_full ? True : False; + wm_name = x11_get_wm_name(g_x11_dpy); + if (wm_name) + { + RARCH_LOG("[X/EGL]: Window manager is %s.\n", wm_name); + + if (true_full && strcasestr(wm_name, "xfwm")) + { + RARCH_LOG("[X/EGL]: Using override-redirect workaround.\n"); + swa.override_redirect = True; + } + free(wm_name); + } if (video_info->monitor_index) g_x11_screen = video_info->monitor_index - 1; @@ -341,7 +354,7 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, g_x11_win = XCreateWindow(g_x11_dpy, RootWindow(g_x11_dpy, vi->screen), x_off, y_off, width, height, 0, vi->depth, InputOutput, vi->visual, - CWBorderPixel | CWColormap | CWEventMask, + CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &swa); XSetWindowBackground(g_x11_dpy, g_x11_win, 0); @@ -375,7 +388,6 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, RARCH_LOG("[X/EGL]: Using true fullscreen.\n"); XMapRaised(g_x11_dpy, g_x11_win); x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win); - XChangeWindowAttributes(g_x11_dpy, g_x11_win, CWOverrideRedirect, &swa); } else if (fullscreen) { From 9f15e39114430299ab66a143f81dd338f3e9c24f Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Mon, 2 Apr 2018 20:21:32 -0500 Subject: [PATCH 111/517] Also use the original fallback designed for older window managers. --- gfx/common/x11_common.c | 41 ++++++++++++++++++++++++++++++++++ gfx/common/x11_common.h | 2 ++ gfx/drivers_context/x_ctx.c | 2 ++ gfx/drivers_context/xegl_ctx.c | 2 ++ 4 files changed, 47 insertions(+) diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index 1ab16b90b7..6de3e8a3d7 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -685,6 +685,47 @@ void x11_event_queue_check(XEvent *event) XIfEvent(g_x11_dpy, event, x11_wait_notify, NULL); } +static bool x11_check_atom_supported(Display *dpy, Atom atom) +{ + Atom XA_NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", True); + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + Atom *prop; + int i; + + if (XA_NET_SUPPORTED == None) + return false; + + XGetWindowProperty(dpy, DefaultRootWindow(dpy), XA_NET_SUPPORTED, 0, UINT_MAX, False, XA_ATOM, &type, &format,&nitems, &bytes_after, (unsigned char **) &prop); + + if (!prop || type != XA_ATOM) + { + return false; + } + + for (i = 0; i < nitems; i++) + { + if (prop[i] == atom) + { + XFree(prop); + return true; + } + } + + XFree(prop); + + return false; +} + +bool x11_has_net_wm_fullscreen(Display *dpy) +{ + XA_NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + + return x11_check_atom_supported(dpy, XA_NET_WM_STATE_FULLSCREEN); +} + char *x11_get_wm_name(Display *dpy) { Atom XA_NET_SUPPORTING_WM_CHECK = XInternAtom(g_x11_dpy, "_NET_SUPPORTING_WM_CHECK", False); diff --git a/gfx/common/x11_common.h b/gfx/common/x11_common.h index ec4fa0dc28..6be78245d1 100644 --- a/gfx/common/x11_common.h +++ b/gfx/common/x11_common.h @@ -77,5 +77,7 @@ void x11_event_queue_check(XEvent *event); char *x11_get_wm_name(Display *dpy); +bool x11_has_net_wm_fullscreen(Display *dpy); + #endif diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index 387336f6a9..e623f6c287 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -705,6 +705,8 @@ static bool gfx_ctx_x_set_video_mode(void *data, } free(wm_name); } + if (!x11_has_net_wm_fullscreen(g_x11_dpy) && true_full) + swa.override_redirect = True; if (video_info->monitor_index) g_x11_screen = video_info->monitor_index - 1; diff --git a/gfx/drivers_context/xegl_ctx.c b/gfx/drivers_context/xegl_ctx.c index 2f120ad956..04485648e2 100644 --- a/gfx/drivers_context/xegl_ctx.c +++ b/gfx/drivers_context/xegl_ctx.c @@ -324,6 +324,8 @@ static bool gfx_ctx_xegl_set_video_mode(void *data, } free(wm_name); } + if (!x11_has_net_wm_fullscreen(g_x11_dpy) && true_full) + swa.override_redirect = True; if (video_info->monitor_index) g_x11_screen = video_info->monitor_index - 1; From 00a3aba480b1946325274b4ba4095f635f29116e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 3 Apr 2018 03:48:59 +0200 Subject: [PATCH 112/517] Define HAVE_RUNAHEAD for dynamic linked iOS --- pkg/apple/RetroArch_iOS10.xcodeproj/project.pbxproj | 5 +++++ pkg/apple/RetroArch_iOS11.xcodeproj/project.pbxproj | 5 +++++ pkg/apple/RetroArch_iOS6.xcodeproj/project.pbxproj | 5 +++++ pkg/apple/RetroArch_iOS8.xcodeproj/project.pbxproj | 5 +++++ pkg/apple/RetroArch_iOS9.xcodeproj/project.pbxproj | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/pkg/apple/RetroArch_iOS10.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_iOS10.xcodeproj/project.pbxproj index 5a76d298f8..b7d7273f6a 100644 --- a/pkg/apple/RetroArch_iOS10.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_iOS10.xcodeproj/project.pbxproj @@ -332,6 +332,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -417,6 +418,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -469,6 +471,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -555,6 +558,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -625,6 +629,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", diff --git a/pkg/apple/RetroArch_iOS11.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_iOS11.xcodeproj/project.pbxproj index 5c0b588200..b1a0bbe5f7 100644 --- a/pkg/apple/RetroArch_iOS11.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_iOS11.xcodeproj/project.pbxproj @@ -332,6 +332,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -418,6 +419,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -471,6 +473,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -558,6 +561,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -628,6 +632,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", diff --git a/pkg/apple/RetroArch_iOS6.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_iOS6.xcodeproj/project.pbxproj index ce77a3519e..0e8c0d9496 100644 --- a/pkg/apple/RetroArch_iOS6.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_iOS6.xcodeproj/project.pbxproj @@ -344,6 +344,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -425,6 +426,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -473,6 +475,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -554,6 +557,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -623,6 +627,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", diff --git a/pkg/apple/RetroArch_iOS8.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_iOS8.xcodeproj/project.pbxproj index 713811bdd1..bf4b24262e 100644 --- a/pkg/apple/RetroArch_iOS8.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_iOS8.xcodeproj/project.pbxproj @@ -347,6 +347,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -416,6 +417,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -490,6 +492,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -574,6 +577,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -625,6 +629,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", diff --git a/pkg/apple/RetroArch_iOS9.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_iOS9.xcodeproj/project.pbxproj index 30534c7f2e..6def3a6b4d 100644 --- a/pkg/apple/RetroArch_iOS9.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_iOS9.xcodeproj/project.pbxproj @@ -362,6 +362,7 @@ "-DHAVE_CORETEXT", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -445,6 +446,7 @@ "-DHAVE_CORETEXT", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -495,6 +497,7 @@ "-DHAVE_CORETEXT", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -580,6 +583,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", @@ -649,6 +653,7 @@ "-DHAVE_HID", "-DHAVE_NETWORKING", "-DHAVE_AVFOUNDATION", + "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_STB_VORBIS", "-DHAVE_MINIUPNPC", From 55450a5d73b26180187a05b70f3f87ec114d763c Mon Sep 17 00:00:00 2001 From: retro-wertz Date: Tue, 3 Apr 2018 12:08:53 +0800 Subject: [PATCH 113/517] Fix false [ERROR] when closing content filestream_delete() returns 0 on success, and -1 on failure. This PR removes this false error when closing content. e.g. [ERROR] Failed to remove temporary file: /EMULATORS/ConsoleRoms/Nintendo - Nintendo Entertainment System/Super Mario Bros. (World).nes. --- tasks/task_content.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/task_content.c b/tasks/task_content.c index 982f9dd54c..2c5f250482 100644 --- a/tasks/task_content.c +++ b/tasks/task_content.c @@ -1820,7 +1820,7 @@ void content_deinit(void) RARCH_LOG("%s: %s.\n", msg_hash_to_str(MSG_REMOVING_TEMPORARY_CONTENT_FILE), path); - if (!filestream_delete(path)) + if (filestream_delete(path) != 0) RARCH_ERR("%s: %s.\n", msg_hash_to_str(MSG_FAILED_TO_REMOVE_TEMPORARY_FILE), path); From af08e5015ad4ad18d7c4c370385a60a16ac3e335 Mon Sep 17 00:00:00 2001 From: gblues Date: Mon, 2 Apr 2018 23:16:49 -0700 Subject: [PATCH 114/517] More work on Dual Shock 3 driver == DETAILS - update to not try starting the read loop until after the device is successfully initialized - add new HID wrapper macros needed by ds3 driver - add some debug logging to help with troubleshooting - add button map for DS3 == TESTING Tested with local build. DS3 init is not working. --- input/common/hid/device_ds3.c | 168 ++++++++++++++++++++++++++- input/common/hid/hid_device_driver.c | 3 + input/include/hid_driver.h | 6 + input/input_autodetect_builtin.c | 1 + wiiu/input/wiiu_hid.c | 17 ++- 5 files changed, 190 insertions(+), 5 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index 3c1a805d60..7ff4ae307b 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -22,33 +22,158 @@ typedef struct ds3_instance { void *handle; joypad_connection_t *pad; + int slot; + bool led_set; + uint32_t buttons; + uint16_t motors[2]; + uint8_t data[64]; } ds3_instance_t; static uint8_t activation_packet[] = { +#if defined(IOS) + 0x53, 0xF4, +#elif defined(HAVE_WIIUSB_HID) + 0x02, +#endif 0x42, 0x0c, 0x00, 0x00 }; +#if defined(WIIU) +#define PACKET_OFFSET 2 +#elif defined(HAVE_WIIUSB_HID) +#define PACKET_OFFSET 1 +#else +#define PACKET_OFFSET 0 +#endif + +#define LED_OFFSET 11 +#define MOTOR1_OFFSET 4 +#define MOTOR2_OFFSET 6 + +static uint8_t control_packet[] = { + 0x52, 0x01, + 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 +}; + +static int control_packet_size = sizeof(control_packet); + extern pad_connection_interface_t ds3_pad_connection; +static void print_error(const char *fmt, int32_t errcode) +{ + int16_t err1, err2; + + err1 = errcode & 0x0000ffff; + err2 = ((errcode & 0xffff0000) >> 16); + + RARCH_ERR(fmt, err1, err2); +} + +static uint32_t send_activation_packet(ds3_instance_t *instance) +{ + uint32_t result; +#if defined(WIIU) + result = HID_SET_REPORT(instance->handle, + HID_REPORT_FEATURE, + DS3_ACTIVATION_REPORT_ID, + activation_packet, + sizeof(activation_packet)); +#else + HID_SEND_CONTROL(instance->handle, + activation_packet, sizeof(activation_packet)); +#endif + if(result) + print_error("[ds3]: activation packet failed (%d:%d)\n", result); + return result; +} + +static uint32_t set_protocol(ds3_instance_t *instance, int protocol) +{ + uint32_t result = 0; +#if defined(WIIU) + result = HID_SET_PROTOCOL(1); + if(result) + print_error("[ds3]: set protocol failed (%d:%d)\n", result); + +#endif + return result; +} + +static uint32_t send_control_packet(ds3_instance_t *instance) +{ + uint8_t packet_buffer[control_packet_size]; + uint32_t result = 0; + memcpy(packet_buffer, control_packet, control_packet_size); + + packet_buffer[LED_OFFSET] = 0; + if(instance->pad) { + packet_buffer[LED_OFFSET] = 1 << ((instance->slot % 4) + 1); + } + packet_buffer[MOTOR1_OFFSET] = instance->motors[1] >> 8; + packet_buffer[MOTOR2_OFFSET] = instance->motors[0] >> 8; + +#if defined(HAVE_WIIUSB_HID) + packet_buffer[1] = 0x03; +#endif + +#if defined(WIIU) + result = HID_SET_REPORT(instance->handle, + HID_REPORT_OUTPUT, + DS3_RUMBLE_REPORT_ID, + packet_buffer+PACKET_OFFSET, + control_packet_size-PACKET_OFFSET); + if(result) + print_error("[ds3]: send control packet failed: (%d:%d)\n", result); +#else + HID_SEND_CONTROL(instance->handle, + packet_buffer+PACKET_OFFSET, + control_packet_size-PACKET_OFFSET); +#endif /* WIIU */ + return result; +} + static void *ds3_init(void *handle) { ds3_instance_t *instance; - + int errors = 0; + RARCH_LOG("[ds3]: init\n"); instance = (ds3_instance_t *)calloc(1, sizeof(ds3_instance_t)); if(!instance) goto error; instance->handle = handle; -/* TODO: do whatever is needed so that the read loop doesn't bomb out */ + RARCH_LOG("[ds3]: sending activation packet\n"); + if(send_activation_packet(instance)) + errors++; + RARCH_LOG("[ds3]: setting protocol\n"); + if(set_protocol(instance, 1)) + errors++; + RARCH_LOG("[ds3]: sending control packet\n"); + if(send_control_packet(instance)) + errors++; + + if(errors) + goto error; instance->pad = hid_pad_register(instance, &ds3_pad_connection); if(!instance->pad) goto error; + RARCH_LOG("[ds3]: init complete.\n"); return instance; error: + RARCH_ERR("[ds3]: init failed.\n"); if(instance) free(instance); return NULL; @@ -62,9 +187,14 @@ static void ds3_free(void *data) free(instance); } -static void ds3_handle_packet(void *data, uint8_t *buffer, size_t size) +static void ds3_handle_packet(void *data, uint8_t *packet, size_t size) { ds3_instance_t *instance = (ds3_instance_t *)data; + + if(!instance || !instance->pad) + return; + + instance->pad->iface->packet_handler(data, packet, size); } static bool ds3_detect(uint16_t vendor_id, uint16_t product_id) @@ -86,6 +216,9 @@ hid_device_t ds3_hid_device = { static void *ds3_pad_init(void *data, uint32_t slot, hid_driver_t *driver) { + ds3_instance_t *pad = (ds3_instance_t *)data; + pad->slot = slot; + return data; } @@ -97,11 +230,38 @@ static void ds3_pad_deinit(void *data) static void ds3_get_buttons(void *data, retro_bits_t *state) { ds3_instance_t *pad = (ds3_instance_t *)data; + + if(pad) + { + BITS_COPY16_PTR(state, pad->buttons); + + if(pad->buttons & 0x10000) + BIT256_SET_PTR(state, RARCH_MENU_TOGGLE); + } else { + BIT256_CLEAR_ALL_PTR(state); + } } static void ds3_packet_handler(void *data, uint8_t *packet, uint16_t size) { - ds3_instance_t *pad = (ds3_instance_t *)data; + ds3_instance_t *instance = (ds3_instance_t *)data; + RARCH_LOG_BUFFER(packet, size); + + if(!instance->led_set) + { + send_activation_packet(instance); + instance->led_set = true; + } + + if(size > control_packet_size) + { + RARCH_ERR("[ds3]: Expecting packet to be %d but was %d\n", + control_packet_size, size); + return; + } + + memcpy(instance->data, packet, size); + instance->buttons = 0; } static void ds3_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c index ee0ef38a58..37bc4feaa8 100644 --- a/input/common/hid/hid_device_driver.c +++ b/input/common/hid/hid_device_driver.c @@ -46,7 +46,10 @@ joypad_connection_t *hid_pad_register(void *pad_handle, pad_connection_interface slot = pad_connection_find_vacant_pad(hid_instance.pad_list); if(slot < 0) + { + RARCH_ERR("[hid]: failed to find a vacant pad.\n"); return NULL; + } result = &(hid_instance.pad_list[slot]); result->iface = iface; diff --git a/input/include/hid_driver.h b/input/include/hid_driver.h index 654c3849ea..577dbb093a 100644 --- a/input/include/hid_driver.h +++ b/input/include/hid_driver.h @@ -60,6 +60,12 @@ struct hid_driver hid_instance.os_driver_data, pad, axis) #define HID_PAD_NAME(pad) \ hid_instance.os_driver->name(hid_instance.os_driver_data, pad) +#define HID_SET_PROTOCOL(protocol) \ + hid_instance.os_driver->set_protocol(hid_instance.os_driver_data, protocol) +#define HID_SET_REPORT(pad, rpttype, rptid, data, len) \ + hid_instance.os_driver->set_report(pad, rpttype, rptid, data, len) +#define HID_SEND_CONTROL(pad, data, len) \ + hid_instance.os_driver->send_control(pad, data, len) #define HID_POLL() hid_instance.os_driver->poll( \ hid_instance.os_driver_data) #define HID_MAX_SLOT() hid_instance.max_slot diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index 2a237e8831..77a09e0487 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -635,6 +635,7 @@ const char* const input_builtin_autoconfs[] = DECL_AUTOCONF_DEVICE(PAD_NAME_CLASSIC, "wiiu", WIIUINPUT_CLASSIC_CONTROLLER_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE(PAD_NAME_HID, "wiiu", WIIUINPUT_GAMEPAD_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE("GameCube Controller", "wiiu", WIIUINPUT_GAMECUBE_DEFAULT_BINDS), + DECL_AUTOCONF_DEVICE("Sony DualShock 3", "wiiu", PS3INPUT_DEFAULT_BINDS), #endif #ifdef __CELLOS_LV2__ DECL_AUTOCONF_DEVICE("SixAxis Controller", "ps3", PS3INPUT_DEFAULT_BINDS), diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index b289f648f4..eaa9210e68 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -458,7 +458,6 @@ static void wiiu_hid_attach(wiiu_hid_t *hid, wiiu_attach_event *event) adapter->state = ADAPTER_STATE_NEW; synchronized_add_to_adapters_list(adapter); - wiiu_start_read_loop(adapter); return; @@ -580,6 +579,21 @@ static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list) } } +static void wiiu_handle_ready_adapters(wiiu_hid_t *hid) +{ + wiiu_adapter_t *it; + OSFastMutex_Lock(&(adapters.lock)); + + for(it = adapters.list; it != NULL; it = it->next) + { + if(it->state == ADAPTER_STATE_READY) + wiiu_start_read_loop(it); + } + + OSFastMutex_Unlock(&(adapters.lock)); + +} + static int wiiu_hid_polling_thread(int argc, const char **argv) { wiiu_hid_t *hid = (wiiu_hid_t *)argv; @@ -590,6 +604,7 @@ static int wiiu_hid_polling_thread(int argc, const char **argv) while(!hid->polling_thread_quit) { wiiu_handle_attach_events(hid, synchronized_get_events_list()); + wiiu_handle_ready_adapters(hid); usleep(10000); i += 10000; if(i >= (1000 * 1000 * 3)) From 852c8735e6ee00d666b6012ed60c1e718da90f43 Mon Sep 17 00:00:00 2001 From: David Walters Date: Tue, 3 Apr 2018 18:21:10 +0100 Subject: [PATCH 115/517] Fix solution file to be Visual Studio 14 / "2015" format. --- pkg/msvc/RetroArch-msvc2015.sln | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/msvc/RetroArch-msvc2015.sln b/pkg/msvc/RetroArch-msvc2015.sln index 2ee30da985..82f4c38f59 100644 --- a/pkg/msvc/RetroArch-msvc2015.sln +++ b/pkg/msvc/RetroArch-msvc2015.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2015 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RetroArch-msvc2015", "msvc-2015\RetroArch-msvc2015.vcxproj", "{27FF7CE1-4059-4AA1-8062-FD529560FA54}" EndProject Global From 7adec98cd84fabe60ab2f4173ccbf7f3ae8c08bc Mon Sep 17 00:00:00 2001 From: David Walters Date: Tue, 3 Apr 2018 18:25:23 +0100 Subject: [PATCH 116/517] Fix MSVC2015 project/solution build config associations --- pkg/msvc/RetroArch-msvc2015.sln | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/msvc/RetroArch-msvc2015.sln b/pkg/msvc/RetroArch-msvc2015.sln index 82f4c38f59..a20ab722df 100644 --- a/pkg/msvc/RetroArch-msvc2015.sln +++ b/pkg/msvc/RetroArch-msvc2015.sln @@ -19,16 +19,20 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug Cg|Win32.ActiveCfg = Debug Cg|Win32 {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug Cg|Win32.Build.0 = Debug Cg|Win32 - {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug Cg|x64.ActiveCfg = Debug Cg|Win32 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug Cg|x64.ActiveCfg = Debug Cg|x64 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug Cg|x64.Build.0 = Debug Cg|x64 {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug|Win32.ActiveCfg = Debug|Win32 {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug|Win32.Build.0 = Debug|Win32 - {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug|x64.ActiveCfg = Debug|Win32 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug|x64.ActiveCfg = Debug|x64 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug|x64.Build.0 = Debug|x64 {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release Cg|Win32.ActiveCfg = Release Cg|Win32 {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release Cg|Win32.Build.0 = Release Cg|Win32 - {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release Cg|x64.ActiveCfg = Release Cg|Win32 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release Cg|x64.ActiveCfg = Release Cg|x64 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release Cg|x64.Build.0 = Release Cg|x64 {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release|Win32.ActiveCfg = Release|Win32 {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release|Win32.Build.0 = Release|Win32 - {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release|x64.ActiveCfg = Release|Win32 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release|x64.ActiveCfg = Release|x64 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From c8a9f5eded7b3f10d72a5538b18530a0cda85f50 Mon Sep 17 00:00:00 2001 From: David Walters Date: Tue, 3 Apr 2018 18:33:48 +0100 Subject: [PATCH 117/517] Compile fix for _vsnprintf_s, missing standard library header. --- libretro-common/compat/compat_snprintf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libretro-common/compat/compat_snprintf.c b/libretro-common/compat/compat_snprintf.c index f31de72d8c..40734a6fe2 100644 --- a/libretro-common/compat/compat_snprintf.c +++ b/libretro-common/compat/compat_snprintf.c @@ -25,6 +25,7 @@ #include +#include /* added for _vsnprintf_s and _vscprintf on VS2015 and VS2017 */ #include #if _MSC_VER < 1800 From b5b906bc5a1f7cc5d2e1f99d8ddbd03a1d8d81b1 Mon Sep 17 00:00:00 2001 From: David Walters Date: Tue, 3 Apr 2018 18:41:34 +0100 Subject: [PATCH 118/517] Fix for fatal error C1128 in 64-bit Debug builds (by adding /bigobj to command line) --- pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj index 981d2f2fc9..a4cf7d356e 100644 --- a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj +++ b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj @@ -384,9 +384,12 @@ CompileAsC - + + /bigobj %(AdditionalOptions) + /bigobj %(AdditionalOptions) + - + \ No newline at end of file From ad0a36b8259ae75103317852e58faf096f1df0cc Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Thu, 5 Apr 2018 00:52:46 +0200 Subject: [PATCH 119/517] XMB thumbnails vertical disposition. --- config.def.h | 2 + configuration.c | 1 + configuration.h | 1 + intl/msg_hash_lbl.h | 2 + intl/msg_hash_us.h | 5 + menu/cbs/menu_cbs_sublabel.c | 4 + menu/drivers/xmb.c | 416 ++++++++++++++++++++++++++++------- menu/menu_displaylist.c | 3 + menu/menu_setting.c | 15 ++ msg_hash.h | 1 + 10 files changed, 367 insertions(+), 83 deletions(-) diff --git a/config.def.h b/config.def.h index 6223deddcf..48041da022 100644 --- a/config.def.h +++ b/config.def.h @@ -654,6 +654,8 @@ static const unsigned menu_thumbnails_default = 3; static const unsigned menu_left_thumbnails_default = 0; +static const bool xmb_vertical_thumbnails = false; + #ifdef IOS static const bool ui_companion_start_on_boot = false; #else diff --git a/configuration.c b/configuration.c index c37036be3d..9f0eed8a04 100644 --- a/configuration.c +++ b/configuration.c @@ -1331,6 +1331,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, #endif #ifdef HAVE_XMB SETTING_BOOL("xmb_shadows_enable", &settings->bools.menu_xmb_shadows_enable, true, xmb_shadows_enable, false); + SETTING_BOOL("xmb_vertical_thumbnails", &settings->bools.menu_xmb_vertical_thumbnails, true, xmb_vertical_thumbnails, false); #endif #endif #ifdef HAVE_CHEEVOS diff --git a/configuration.h b/configuration.h index 61909d8a95..b43facf5f6 100644 --- a/configuration.h +++ b/configuration.h @@ -148,6 +148,7 @@ typedef struct settings bool menu_show_reboot; bool menu_materialui_icons_enable; bool menu_xmb_shadows_enable; + bool menu_xmb_vertical_thumbnails; bool menu_content_show_settings; bool menu_content_show_favorites; bool menu_content_show_images; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 88ce40ea04..b5f61b291e 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1037,6 +1037,8 @@ MSG_HASH(MENU_ENUM_LABEL_THUMBNAILS, "thumbnails") MSG_HASH(MENU_ENUM_LABEL_LEFT_THUMBNAILS, "left thumbnails") +MSG_HASH(MENU_ENUM_LABEL_XMB_VERTICAL_THUMBNAILS, + "xmb_vertical_thumbnails") MSG_HASH(MENU_ENUM_LABEL_THUMBNAILS_DIRECTORY, "thumbnails_directory") MSG_HASH(MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 5ce8ae6e58..a4c4facccf 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1633,6 +1633,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS, "Thumbnails") MSG_HASH(MENU_ENUM_LABEL_VALUE_LEFT_THUMBNAILS, "Left Thumbnails") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_VERTICAL_THUMBNAILS, + "Thumbnails Vertical Disposition") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS_DIRECTORY, "Thumbnails") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS_UPDATER_LIST, @@ -2932,6 +2934,9 @@ MSG_HASH( MENU_ENUM_SUBLABEL_LEFT_THUMBNAILS, "Type of thumbnail to display at the left." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_XMB_VERTICAL_THUMBNAILS, + "Display the left thumbnail under the right one, on the right side of the screen.") MSG_HASH( MENU_ENUM_SUBLABEL_TIMEDATE_ENABLE, "Shows current date and/or time inside the menu." diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 9db59de319..25b3ae9aed 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -297,6 +297,7 @@ default_sublabel_macro(action_bind_sublabel_disk_options, default_sublabel_macro(action_bind_sublabel_menu_throttle_framerate, MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE) default_sublabel_macro(action_bind_sublabel_xmb_icon_theme, MENU_ENUM_SUBLABEL_XMB_THEME) default_sublabel_macro(action_bind_sublabel_xmb_shadows_enable, MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE) +default_sublabel_macro(action_bind_sublabel_xmb_vertical_thumbnails, MENU_ENUM_SUBLABEL_XMB_VERTICAL_THUMBNAILS) default_sublabel_macro(action_bind_sublabel_menu_color_theme, MENU_ENUM_SUBLABEL_MATERIALUI_MENU_COLOR_THEME) default_sublabel_macro(action_bind_sublabel_menu_wallpaper_opacity, MENU_ENUM_SUBLABEL_MENU_WALLPAPER_OPACITY) default_sublabel_macro(action_bind_sublabel_menu_framebuffer_opacity, MENU_ENUM_SUBLABEL_MENU_FRAMEBUFFER_OPACITY) @@ -765,6 +766,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_XMB_SHADOWS_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_xmb_shadows_enable); break; + case MENU_ENUM_LABEL_XMB_VERTICAL_THUMBNAILS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_xmb_vertical_thumbnails); + break; case MENU_ENUM_LABEL_XMB_THEME: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_xmb_icon_theme); break; diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 9e80415cba..f025ff086b 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -2,7 +2,6 @@ * Copyright (C) 2011-2017 - Daniel De Matteis * Copyright (C) 2014-2017 - Jean-André Santoni * Copyright (C) 2016-2017 - Brad Parker - * Copyright (C) 2018 - Alfredo Monclús * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- @@ -3048,8 +3047,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) return; scale_factor = (settings->uints.menu_xmb_scale_factor * (float)width) / (1920.0 * 100); - pseudo_font_length = xmb->icon_spacing_horizontal - * 4 - xmb->icon_size / 4; + pseudo_font_length = xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4; xmb->frame_count++; @@ -3120,123 +3118,161 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) menu_display_rotate_z(&rotate_draw, video_info); menu_display_blend_begin(video_info); - /* Do not draw the right thumbnail if there is no space available */ - - if (((xmb->margins_screen_top + - xmb->icon_size + min_thumb_size) <= height) && - ((xmb->margins_screen_left * scale_mod[5] + - xmb->icon_spacing_horizontal + - pseudo_font_length + min_thumb_size) <= width)) + /* Save State thumbnail, right side */ + if (xmb->savestate_thumbnail) { - if (xmb->savestate_thumbnail) - xmb_draw_thumbnail(video_info, - xmb, &coord_white[0], width, height, - xmb->margins_screen_left * scale_mod[5] - + xmb->icon_spacing_horizontal + pseudo_font_length, - xmb->margins_screen_top + xmb->icon_size - + xmb->savestate_thumbnail_height * scale_mod[4], - xmb->savestate_thumbnail_width * scale_mod[4], - xmb->savestate_thumbnail_height * scale_mod[4], - xmb->savestate_thumbnail); - else if (xmb->thumbnail - && !string_is_equal(xmb_thumbnails_ident('R'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + pseudo_font_length, + xmb->margins_screen_top + xmb->icon_size + + xmb->savestate_thumbnail_height * scale_mod[4], + xmb->savestate_thumbnail_width * scale_mod[4], + xmb->savestate_thumbnail_height * scale_mod[4], + xmb->savestate_thumbnail); + } + + menu_display_blend_end(video_info); + menu_display_blend_begin(video_info); + + /* Right thumbnail big size */ + if (!settings->bools.menu_xmb_vertical_thumbnails || + (settings->bools.menu_xmb_vertical_thumbnails && !xmb->left_thumbnail)) + { + /* Do not draw the right thumbnail if there is no space available */ + + if (((xmb->margins_screen_top + + xmb->icon_size + min_thumb_size) <= height) && + ((xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) { - - /* Limit thumbnail width */ - float thumb_width = 0.0f; - float thumb_height = 0.0f; - float thumb_max_width = (float)width - (xmb->icon_size / 6) - - (xmb->margins_screen_left * scale_mod[5]) - - xmb->icon_spacing_horizontal - pseudo_font_length; - -#ifdef XMB_DEBUG - RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", - xmb->thumbnail_width, xmb->thumbnail_height); - RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); -#endif - - if (xmb->thumbnail_width * scale_mod[4] > thumb_max_width) + if (xmb->thumbnail + && !string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { - thumb_width = (xmb->thumbnail_width * scale_mod[4]) * - (thumb_max_width / (xmb->thumbnail_width * scale_mod[4])); - thumb_height = (xmb->thumbnail_height * scale_mod[4]) * - (thumb_max_width / (xmb->thumbnail_width * scale_mod[4])); - } - else - { - thumb_width = xmb->thumbnail_width * scale_mod[4]; - thumb_height = xmb->thumbnail_height * scale_mod[4]; - } + /* Limit thumbnail width */ - /* Limit thumbnail height to screen height + margin. */ + float thumb_width = 0.0f; + float thumb_height = 0.0f; + float thumb_max_width = (float)width - (xmb->icon_size / 6) + - (xmb->margins_screen_left * scale_mod[5]) - + xmb->icon_spacing_horizontal - pseudo_font_length; - if (xmb->margins_screen_top + xmb->icon_size + thumb_height >= - ((float)height * under_thumb_margin)) - { - thumb_width = thumb_width * - ((((float)height * under_thumb_margin) - - xmb->margins_screen_top - xmb->icon_size) / - thumb_height); - thumb_height = thumb_height * - ((((float)height * under_thumb_margin) - - xmb->margins_screen_top - xmb->icon_size) / - thumb_height); + #ifdef XMB_DEBUG + RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", + xmb->thumbnail_width, xmb->thumbnail_height); + RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (xmb->thumbnail_width * scale_mod[4] > thumb_max_width) + { + thumb_width = (xmb->thumbnail_width * scale_mod[4]) * + (thumb_max_width / (xmb->thumbnail_width * scale_mod[4])); + thumb_height = (xmb->thumbnail_height * scale_mod[4]) * + (thumb_max_width / (xmb->thumbnail_width * scale_mod[4])); + } + else + { + thumb_width = xmb->thumbnail_width * scale_mod[4]; + thumb_height = xmb->thumbnail_height * scale_mod[4]; + } + + /* Limit thumbnail height to screen height + margin. */ + + if (xmb->margins_screen_top + xmb->icon_size + thumb_height >= + ((float)height * under_thumb_margin)) + { + thumb_width = thumb_width * + ((((float)height * under_thumb_margin) - + xmb->margins_screen_top - xmb->icon_size) / + thumb_height); + thumb_height = thumb_height * + ((((float)height * under_thumb_margin) - + xmb->margins_screen_top - xmb->icon_size) / + thumb_height); + } + + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + (float)width - (xmb->icon_size / 6) - thumb_max_width + + ((thumb_max_width - thumb_width) / 2), + xmb->margins_screen_top + xmb->icon_size + thumb_height, + thumb_width, thumb_height, + xmb->thumbnail); } - - xmb_draw_thumbnail(video_info, - xmb, &coord_white[0], width, height, - (float)width - (xmb->icon_size / 6) - thumb_max_width + - ((thumb_max_width - thumb_width) / 2), - xmb->margins_screen_top + xmb->icon_size + thumb_height, - thumb_width, thumb_height, - xmb->thumbnail); } } - /* Do not draw the left thumbnail if there is no space available */ + menu_display_blend_end(video_info); + menu_display_blend_begin(video_info); - if ((xmb->margins_screen_top + xmb->icon_size * - (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) + /* Left thumbnail in the left margin */ + /* Do not draw the left thumbnail if there is no space available */ + if (!settings->bools.menu_xmb_vertical_thumbnails && + (xmb->margins_screen_top + xmb->icon_size * + (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) <= (float)height) { - /* Left Thumbnail */ + /* Left Thumbnail in the left margin */ if (xmb->left_thumbnail && !string_is_equal(xmb_thumbnails_ident('L'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { - float left_thumb_width = 0.0f; - float left_thumb_height = 0.0f; + /* Limit left thumbnail width */ + + float left_thumb_width = 0.0f; + float left_thumb_height = 0.0f; + float thumb_max_width = xmb->icon_size * 3.4; + + #ifdef XMB_DEBUG + RARCH_LOG("[XMB left thumbnail] width: %.2f, height: %.2f\n", + xmb->left_thumbnail_width, xmb->left_thumbnail_height); + RARCH_LOG("[XMB left thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (xmb->left_thumbnail_width * scale_mod[4] > thumb_max_width) + { + left_thumb_width = (xmb->left_thumbnail_width * scale_mod[4]) * + (thumb_max_width / (xmb->left_thumbnail_width * scale_mod[4])); + left_thumb_height = (xmb->left_thumbnail_height * scale_mod[4]) * + (thumb_max_width / (xmb->left_thumbnail_width * scale_mod[4])); + } + else + { + left_thumb_width = xmb->left_thumbnail_width * scale_mod[4]; + left_thumb_height = xmb->left_thumbnail_height * scale_mod[4]; + } /* Limit left thumbnail height to screen height + margin. */ if (xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + - xmb->left_thumbnail_height >= + left_thumb_height >= ((float)height - (96.0 * scale_factor))) { - left_thumb_width = xmb->left_thumbnail_width * + left_thumb_width = left_thumb_width * ((((float)height - (96.0 * scale_factor)) - xmb->margins_screen_top - (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / - xmb->left_thumbnail_height); + left_thumb_height); - left_thumb_height = xmb->left_thumbnail_height * + left_thumb_height = left_thumb_height * ((((float)height - (96.0 * scale_factor)) - xmb->margins_screen_top - (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / - xmb->left_thumbnail_height); + left_thumb_height); } else { - left_thumb_width = xmb->left_thumbnail_width; - left_thumb_height = xmb->left_thumbnail_height; + left_thumb_width = left_thumb_width; + left_thumb_height = left_thumb_height; } xmb_draw_thumbnail(video_info, xmb, &coord_white[0], width, height, (xmb->icon_size / 6) + - ((xmb->left_thumbnail_width - left_thumb_width) / 2), + ((thumb_max_width - left_thumb_width) / 2), xmb->margins_screen_top + xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1) + left_thumb_height, left_thumb_width, left_thumb_height, @@ -3244,6 +3280,80 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } } + menu_display_blend_end(video_info); + menu_display_blend_begin(video_info); + + /* No Right Thumbnail, draw only the left one big size */ + if (settings->bools.menu_xmb_vertical_thumbnails && !xmb->thumbnail) + { + /* Do not draw the left thumbnail if there is no space available */ + + if (((xmb->margins_screen_top + + xmb->icon_size + min_thumb_size) <= height) && + ((xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) + { + if (xmb->left_thumbnail + && !string_is_equal(xmb_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + /* Limit left thumbnail width */ + + float left_thumb_width = 0.0f; + float left_thumb_height = 0.0f; + float thumb_max_width = (float)width - (xmb->icon_size / 6) + - (xmb->margins_screen_left * scale_mod[5]) - + xmb->icon_spacing_horizontal - pseudo_font_length; + + #ifdef XMB_DEBUG + RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", + xmb->thumbnail_width, xmb->thumbnail_height); + RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (xmb->left_thumbnail_width * scale_mod[4] > thumb_max_width) + { + left_thumb_width = (xmb->left_thumbnail_width * scale_mod[4]) * + (thumb_max_width / (xmb->left_thumbnail_width * scale_mod[4])); + left_thumb_height = (xmb->left_thumbnail_height * scale_mod[4]) * + (thumb_max_width / (xmb->left_thumbnail_width * scale_mod[4])); + } + else + { + left_thumb_width = xmb->left_thumbnail_width * scale_mod[4]; + left_thumb_height = xmb->left_thumbnail_height * scale_mod[4]; + } + + /* Limit left thumbnail height to screen height + margin. */ + + if (xmb->margins_screen_top + xmb->icon_size + left_thumb_height >= + ((float)height * under_thumb_margin)) + { + left_thumb_width = left_thumb_width * + ((((float)height * under_thumb_margin) - + xmb->margins_screen_top - xmb->icon_size) / + left_thumb_height); + left_thumb_height = left_thumb_height * + ((((float)height * under_thumb_margin) - + xmb->margins_screen_top - xmb->icon_size) / + left_thumb_height); + } + + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + (float)width - (xmb->icon_size / 6) - thumb_max_width + + ((thumb_max_width - left_thumb_width) / 2), + xmb->margins_screen_top + xmb->icon_size + left_thumb_height, + left_thumb_width, left_thumb_height, + xmb->left_thumbnail); + } + } + } + + menu_display_blend_end(video_info); + menu_display_blend_begin(video_info); + /* Clock image */ menu_display_set_alpha(coord_white, MIN(xmb->alpha, 1.00f)); @@ -3369,6 +3479,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) &coord_white[0], xmb->shadow_offset); + menu_display_blend_end(video_info); menu_display_blend_begin(video_info); /* Horizontal tab icons */ @@ -3422,6 +3533,143 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } menu_display_blend_end(video_info); + menu_display_blend_begin(video_info); + + /* Right side 2 thumbnails on top of each other */ + /* here to be displayed above the horizontal icons */ + /* Do not draw the right thumbnail if there is no space available */ + if (xmb->left_thumbnail && xmb->thumbnail && settings->bools.menu_xmb_vertical_thumbnails) + { + if (((xmb->margins_screen_top + + xmb->icon_size + min_thumb_size) <= height) && + ((xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) + { + if (xmb->thumbnail && + !string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + /* Limit right thumbnail width */ + + float thumb_width = 0.0f; + float thumb_height = 0.0f; + float thumb_max_width = (float)width - (xmb->icon_size / 6) - + (xmb->margins_screen_left * scale_mod[5]) - + xmb->icon_spacing_horizontal - pseudo_font_length; + + #ifdef XMB_DEBUG + RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", + xmb->thumbnail_width, xmb->thumbnail_height); + RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (xmb->thumbnail_width * scale_mod[4] > thumb_max_width) + { + thumb_width = (xmb->thumbnail_width * scale_mod[4]) * + (thumb_max_width / (xmb->thumbnail_width * scale_mod[4])); + thumb_height = (xmb->thumbnail_height * scale_mod[4]) * + (thumb_max_width / (xmb->thumbnail_width * scale_mod[4])); + } + else + { + thumb_width = xmb->thumbnail_width * scale_mod[4]; + thumb_height = xmb->thumbnail_height * scale_mod[4]; + } + + /* Limit right thumbnail height to usable area. */ + + if (thumb_height >= + ((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) + { + thumb_width = thumb_width * + ((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) / + thumb_height); + thumb_height = thumb_height * + ((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) / + thumb_height); + } + + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + (float)width - (xmb->icon_size / 6) - thumb_max_width + + ((thumb_max_width - thumb_width) / 2), + xmb->icon_size + ((((float)height / 2 - + (xmb->icon_size + (xmb->icon_size/12))) - thumb_height) / 2) + + thumb_height, + thumb_width, thumb_height, + xmb->thumbnail); + } + } + + /* Do not draw the left thumbnail if there is no space available */ + + if (((xmb->margins_screen_top + + xmb->icon_size + min_thumb_size) <= height) && + ((xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) + { + if (xmb->left_thumbnail && + !string_is_equal(xmb_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + /* Limit left thumbnail width */ + + float left_thumb_width = 0.0f; + float left_thumb_height = 0.0f; + float thumb_max_width = (float)width - (xmb->icon_size / 6) - + (xmb->margins_screen_left * scale_mod[5]) - + xmb->icon_spacing_horizontal - pseudo_font_length; + + #ifdef XMB_DEBUG + RARCH_LOG("[XMB left thumbnail] width: %.2f, height: %.2f\n", + xmb->left_thumbnail_width, xmb->left_thumbnail_height); + RARCH_LOG("[XMB left thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (xmb->left_thumbnail_width * scale_mod[4] > thumb_max_width) + { + left_thumb_width = (xmb->left_thumbnail_width * scale_mod[4]) * + (thumb_max_width / (xmb->left_thumbnail_width * scale_mod[4])); + left_thumb_height = (xmb->left_thumbnail_height * scale_mod[4]) * + (thumb_max_width / (xmb->left_thumbnail_width * scale_mod[4])); + } + else + { + left_thumb_width = xmb->left_thumbnail_width * scale_mod[4]; + left_thumb_height = xmb->left_thumbnail_height * scale_mod[4]; + } + + /* Limit left thumbnail height to usable area. */ + + if (left_thumb_height >= + ((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) + { + left_thumb_width = left_thumb_width * + ((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) / + left_thumb_height); + left_thumb_height = left_thumb_height * + ((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) / + left_thumb_height); + } + + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + (float)width - (xmb->icon_size / 6) - thumb_max_width + + ((thumb_max_width - left_thumb_width) / 2), + xmb->icon_size + + (((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) + + (((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) - + left_thumb_height) / 2) + left_thumb_height, + left_thumb_width, left_thumb_height, + xmb->left_thumbnail); + } + } + } + + menu_display_blend_end(video_info); + menu_display_blend_begin(video_info); /* Vertical icons */ if (xmb) @@ -3450,6 +3698,8 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) width, height); + menu_display_blend_end(video_info); + font_driver_flush(video_info->width, video_info->height, xmb->font, video_info); font_driver_bind_block(xmb->font, NULL); @@ -3532,7 +3782,7 @@ static void xmb_layout_ps3(xmb_handle_t *xmb, int width) xmb->thumbnail_width = 1024.0 * scale_factor; - xmb->left_thumbnail_width = 430.0 * scale_factor; + xmb->left_thumbnail_width = 1024.0 * scale_factor; xmb->savestate_thumbnail_width= 460.0 * scale_factor; xmb->cursor_size = 64.0 * scale_factor; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index f7d85028dd..c77acb2af6 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5347,6 +5347,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_LEFT_THUMBNAILS, PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_VERTICAL_THUMBNAILS, + PARSE_ONLY_BOOL, false); info->need_refresh = true; info->need_push = true; diff --git a/menu/menu_setting.c b/menu/menu_setting.c index f076d5bd2e..fd3ecddf86 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -6086,6 +6086,21 @@ static bool setting_append_list( general_write_handler, general_read_handler); menu_settings_list_current_add_range(list, list_info, 0, 3, 1, true, true); + + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_xmb_vertical_thumbnails, + MENU_ENUM_LABEL_XMB_VERTICAL_THUMBNAILS, + MENU_ENUM_LABEL_VALUE_XMB_VERTICAL_THUMBNAILS, + xmb_vertical_thumbnails, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); } CONFIG_BOOL( diff --git a/msg_hash.h b/msg_hash.h index 781fb6d757..0f9c21b9ce 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -774,6 +774,7 @@ enum msg_hash_enums MENU_LABEL(XMB_RIBBON_ENABLE), MENU_LABEL(THUMBNAILS), MENU_LABEL(LEFT_THUMBNAILS), + MENU_LABEL(XMB_VERTICAL_THUMBNAILS), MENU_LABEL(TIMEDATE_ENABLE), MENU_LABEL(BATTERY_LEVEL_ENABLE), MENU_LABEL(MATERIALUI_MENU_COLOR_THEME), From dc901f6e1e54c9268eabb6d68f2d7434f6f55980 Mon Sep 17 00:00:00 2001 From: altiereslima Date: Thu, 5 Apr 2018 11:41:52 -0300 Subject: [PATCH 120/517] Updated Portuguese-BR translation --- intl/msg_hash_pt_br.h | 123 +++++++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 31 deletions(-) diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index 109f33450a..5bdce6348e 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -316,6 +316,14 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_DESCRIPTION, MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, "Conquistas no Modo Hardcore" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CHEEVOS_LEADERBOARDS_ENABLE, + "Tabelas de Classificação" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CHEEVOS_BADGES_ENABLE, + "Insígnias de Conquistas" + ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ACHIEVEMENTS, "Conquistas Bloqueadas:" ) @@ -1189,14 +1197,32 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD, "Senha do Servidor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE, - "Anunciar Netplay Publicamente" - ) + "Anunciar Netplay Publicamente") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUEST_DEVICE_I, + "Solicitar Dispositivo %u") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUIRE_SLAVES, "Não Permitir Clientes em Modo Não Escravo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS, - "Configurações do Netplay" - ) + "Configurações do Netplay") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG, + "Compartilhamento de Entrada Analógica") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_MAX, + "Máximo") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_AVERAGE, + "Médio") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL, + "Compartilhamento de Entrada Digital") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_OR, + "Compartilhar") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_XOR, + "Agarrar") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_VOTE, + "Eleger") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NONE, + "Nenhum") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NO_PREFERENCE, + "Sem preferência") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR, "Modo Espectador do Netplay" ) @@ -2335,11 +2361,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, "Habilitar ou desabilitar conquistas não oficiais e/ou recursos beta para fins de teste." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, - "Habilitar ou desabilitar Estado de Jogo, Trapaças, Voltar Atrás, Avanço Rápido, Pausa e Câmera Lenta para todos os jogos." - ) + "Habilitar ou desabilitar Estado de Jogo, Trapaças, Voltar Atrás, Avanço Rápido, Pausa e Câmera Lenta para todos os jogos.") +MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_LEADERBOARDS_ENABLE, + "Ativar ou desativar tabelas de classificação no jogo. Não tem efeito se o modo Hardcore estiver desativado.") +MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_BADGES_ENABLE, + "Ativar ou desativar a exibição de insígnia na Lista de Conquistas.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_VERBOSE_ENABLE, - "Habilitar ou desabilitar detalhes das conquistas na tela." - ) + "Habilitar ou desabilitar detalhes das conquistas na tela.") +MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_AUTO_SCREENSHOT, + "Obter automaticamente uma captura de tela quando uma conquista é acionada.") MSG_HASH(MENU_ENUM_SUBLABEL_DRIVER_SETTINGS, "Alterar os drivers utilizados pelo sistema." ) @@ -3570,6 +3600,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Ordenar os Estados de Jogo em pastas com o nome do núcleo utilizado." ) +MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REQUEST_DEVICE_I, + "Solicitar jogar com o dispositivo de entrada dado.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL para o diretório de atualização de núcleos no buildbot do Libreto." ) @@ -3907,8 +3939,11 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_USE_MITM_SERVER, "Utilizar Servidor MITM" ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_USE_MITM_SERVER, - "Encaminhar conexões do Netplay através de um servidor 'homem no meio' (MITM). Útil se o hospedeiro estiver atrás de um firewall ou tiver problemas de NAT/UPnP." - ) + "Encaminhar conexões do Netplay através de um servidor 'homem no meio' (MITM). Útil se o hospedeiro estiver atrás de um firewall ou tiver problemas de NAT/UPnP.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_MITM_SERVER, + "Localização do Servidor de Retransmissão") +MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_MITM_SERVER, + "Escolha um servidor de retransmissão específico para usar. Locais geograficamente mais próximos tendem a ter menor latência.") MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_MIXER, "Adicionar ao mixer" ) @@ -3940,11 +3975,11 @@ MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_ONLINE_UPDATER, "Exibir a opção 'Atualizador Online'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_VIEWS_SETTINGS, - "Visualizações" - ) -MSG_HASH(MENU_ENUM_SUBLABEL_MENU_VIEWS_SETTINGS, - "Exibir elementos na tela de menu." - ) + "Visualizações") +MSG_HASH( + MENU_ENUM_SUBLABEL_MENU_VIEWS_SETTINGS, + "Exibir elementos na tela de menu." + ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_CORE_UPDATER, "Exibir Atualizador de Núcleos" ) @@ -3958,14 +3993,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_DELETE, "Remover núcleo" ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_DELETE, - "Remover este núcleo do disco." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_RENAME_ENTRY, - "Renomear o título do item." - ) -MSG_HASH(MENU_ENUM_LABEL_VALUE_RENAME_ENTRY, - "Renomear" - ) + "Remover este núcleo do disco.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_FRAMEBUFFER_OPACITY, "Opacidade do Framebuffer" ) @@ -4027,8 +4055,11 @@ MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_ENTRY_RENAME, "Permita que o usuário renomeie as entradas nas coleções." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_ENTRY_RENAME, - "Permitir renomear entradas" - ) + "Permitir renomear entradas" ) +MSG_HASH(MENU_ENUM_SUBLABEL_RENAME_ENTRY, + "Renomear o título da entrada.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_RENAME_ENTRY, + "Renomear") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_LOAD_CORE, "Exibir Carregar Núcleo" ) @@ -4141,8 +4172,17 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_INFORMATION, "Exibir Informação" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_INFORMATION, - "Exibir/ocultar a opção 'Informação'." - ) + "Exibir/ocultar a opção 'Informação'.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_ENABLE, + "Ativar Notificação de Fundo") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_RED, + "Notificação de Fundo em Cor Vermelha") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_GREEN, + "Notificação de Fundo em Cor Verde") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_BLUE, + "Notificação de Fundo em Cor Azul") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_OPACITY, + "Opacidade da Notificação de Fundo") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_DISABLE_KIOSK_MODE, "Desabilitar o Modo Quiosque" ) @@ -4168,19 +4208,40 @@ MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD_OK, "Senha correta." ) MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD_NOK, - "Senha incorreta." - ) + "Senha incorreta.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_RED, + "Notificação Cor Vermelha") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_GREEN, + "Notificação Cor Verde") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_BLUE, + "Notificação Cor Azul") +MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAMECOUNT_SHOW, + "Mostrar contagem de quadros na tela FPS") +MSG_HASH(MSG_CONFIG_OVERRIDE_LOADED, + "Substituição de configuração carregada.") +MSG_HASH(MSG_GAME_REMAP_FILE_LOADED, + "Arquivo de remapeamento do jogo carregado.") +MSG_HASH(MSG_CORE_REMAP_FILE_LOADED, + "Arquivo de remapeamento principal carregado.") MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTOMATICALLY_ADD_CONTENT_TO_PLAYLIST, "Adicione automaticamente conteúdo à lista de reprodução") MSG_HASH(MENU_ENUM_SUBLABEL_AUTOMATICALLY_ADD_CONTENT_TO_PLAYLIST, "Verifica automaticamente o conteúdo carregado para que eles apareçam dentro das listas de reprodução.") MSG_HASH(MSG_SCANNING_OF_FILE_FINISHED, "Verificação do arquivo terminado") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_OPACITY, + "Opacidade da Janela") MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_QUALITY, "Qualidade da Reamostragem do Áudio") MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_QUALITY, "Abaixe esse valor para favorecer o desempenho/baixa latência em relação à qualidade de áudio, aumente se desejar melhor qualidade de áudio à custa do desempenho/baixa latência.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_WATCH_FOR_CHANGES, + "Ver arquivos de shader para mudanças") +MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_WATCH_FOR_CHANGES, + "Aplicar automaticamente as alterações feitas nos arquivos de shader no disco.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_SHOW_DECORATIONS, + "Mostrar Decorações da Janela") MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, - "Display Statistics") + "Exibir estatísticas") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, - "Show onscreen technical statistics.") + "Mostrar estatísticas técnicas na tela.") From 1315e26ffa48f58e8db29a1e301a2ad416eb3f14 Mon Sep 17 00:00:00 2001 From: altiereslima Date: Thu, 5 Apr 2018 11:46:11 -0300 Subject: [PATCH 121/517] update --- intl/msg_hash_pt_br.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index 5bdce6348e..fea530b8ba 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -4187,7 +4187,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_DISABLE_KIOSK_MODE, "Desabilitar o Modo Quiosque" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_DISABLE_KIOSK_MODE, - "Desabilita o Modo Quiosque . É necessária uma reinicialização para que a mudança tenha total efeito." + "Desabilita o Modo Quiosque. É necessária uma reinicialização para que a mudança tenha total efeito." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_ENABLE_KIOSK_MODE, "Habilitar o Modo Quiosque" @@ -4210,11 +4210,11 @@ MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD_OK, MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD_NOK, "Senha incorreta.") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_RED, - "Notificação Cor Vermelha") + "Notificação em Cor Vermelha") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_GREEN, - "Notificação Cor Verde") + "Notificação em Cor Verde") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_BLUE, - "Notificação Cor Azul") + "Notificação em Cor Azul") MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAMECOUNT_SHOW, "Mostrar contagem de quadros na tela FPS") MSG_HASH(MSG_CONFIG_OVERRIDE_LOADED, From e78aedbb6c1ed614d341cbc7fa5824ad3e3f5cd7 Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Thu, 5 Apr 2018 17:08:33 +0200 Subject: [PATCH 122/517] undo copyright change --- menu/drivers/xmb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index f025ff086b..23fb367a6f 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -2,6 +2,7 @@ * Copyright (C) 2011-2017 - Daniel De Matteis * Copyright (C) 2014-2017 - Jean-André Santoni * Copyright (C) 2016-2017 - Brad Parker + * Copyright (C) 2018 - Alfredo Monclús * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- From e42edbf03262c2ba3d0196b1cdc668613ccd21c4 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Thu, 5 Apr 2018 15:33:02 -0500 Subject: [PATCH 123/517] Move Callback Init calls to after load content (fixes Mesen crashing) --- runahead/secondary_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 5be3a0f9b2..e1d84b0010 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -202,13 +202,6 @@ bool secondary_core_create(void) if (init_libretro_sym_custom(CORE_TYPE_PLAIN, &secondary_core, secondary_library_path, &secondary_module)) { secondary_core.symbols_inited = true; - - core_set_default_callbacks(&secondary_callbacks); - secondary_core.retro_set_video_refresh(secondary_callbacks.frame_cb); - secondary_core.retro_set_audio_sample(secondary_callbacks.sample_cb); - secondary_core.retro_set_audio_sample_batch(secondary_callbacks.sample_batch_cb); - secondary_core.retro_set_input_state(secondary_callbacks.state_cb); - secondary_core.retro_set_input_poll(secondary_callbacks.poll_cb); secondary_core.retro_set_environment(rarch_environment_secondary_core_hook); secondary_core_set_variable_update(); @@ -267,6 +260,13 @@ bool secondary_core_create(void) secondary_core.retro_set_controller_port_device(port, device); } clear_controller_port_map(); + + core_set_default_callbacks(&secondary_callbacks); + secondary_core.retro_set_video_refresh(secondary_callbacks.frame_cb); + secondary_core.retro_set_audio_sample(secondary_callbacks.sample_cb); + secondary_core.retro_set_audio_sample_batch(secondary_callbacks.sample_batch_cb); + secondary_core.retro_set_input_state(secondary_callbacks.state_cb); + secondary_core.retro_set_input_poll(secondary_callbacks.poll_cb); } else return false; From f37ed2f7e12dc2d8772823a1f9470046b932b80c Mon Sep 17 00:00:00 2001 From: Dwedit Date: Thu, 5 Apr 2018 15:42:20 -0500 Subject: [PATCH 124/517] Move callback init calls to before set_controller_port_device calls. --- runahead/secondary_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index e1d84b0010..65e04b2bf9 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -253,6 +253,13 @@ bool secondary_core_create(void) return false; } + core_set_default_callbacks(&secondary_callbacks); + secondary_core.retro_set_video_refresh(secondary_callbacks.frame_cb); + secondary_core.retro_set_audio_sample(secondary_callbacks.sample_cb); + secondary_core.retro_set_audio_sample_batch(secondary_callbacks.sample_batch_cb); + secondary_core.retro_set_input_state(secondary_callbacks.state_cb); + secondary_core.retro_set_input_poll(secondary_callbacks.poll_cb); + for (port = 0; port < 16; port++) { device = port_map[port]; @@ -260,13 +267,6 @@ bool secondary_core_create(void) secondary_core.retro_set_controller_port_device(port, device); } clear_controller_port_map(); - - core_set_default_callbacks(&secondary_callbacks); - secondary_core.retro_set_video_refresh(secondary_callbacks.frame_cb); - secondary_core.retro_set_audio_sample(secondary_callbacks.sample_cb); - secondary_core.retro_set_audio_sample_batch(secondary_callbacks.sample_batch_cb); - secondary_core.retro_set_input_state(secondary_callbacks.state_cb); - secondary_core.retro_set_input_poll(secondary_callbacks.poll_cb); } else return false; From 1fadc2e14886e0fd5f7b181f51a0da0804eca390 Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Fri, 6 Apr 2018 00:59:35 +0200 Subject: [PATCH 125/517] Fix image preview sometimes on both thumbs. --- menu/drivers/xmb.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 23fb367a6f..ca6d1d3abe 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -999,7 +999,9 @@ static void xmb_update_thumbnail_path(void *data, unsigned i, char pos) xmb_node_t *node = (xmb_node_t*) file_list_get_userdata_at_offset(selection_buf, i); - if (!string_is_empty(node->fullpath)) + if (!string_is_empty(node->fullpath) && + (pos == 'R' || (pos == 'L' && string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))))) { if (!string_is_empty(entry.path)) fill_pathname_join( @@ -1037,8 +1039,8 @@ static void xmb_update_thumbnail_path(void *data, unsigned i, char pos) } else { - xmb->left_thumbnail = 0; - goto end; + xmb->left_thumbnail = 0; + goto end; } } } From 4433cbebc6c9ec8db9561c5e227cdf916d430efe Mon Sep 17 00:00:00 2001 From: gblues Date: Thu, 5 Apr 2018 23:03:38 -0700 Subject: [PATCH 126/517] Get digital inputs for Sony DualShock 3 working == DETAILS - fix the bitshift math - read the right bytes out of the ds3 data packet - remove verbose logging in critical path - stop caring about errors in the hid read loop -- seems to just be benign "device not ready" -- or at least, that's what I'm assuming given that the read eventually succeeds. == TESTING Played Mario 3 with the DS3 with no issues. --- input/common/hid/device_ds3.c | 84 +++++++++++++++++++++++++------- input/include/hid_driver.h | 4 +- input/input_autodetect_builtin.c | 21 +++++++- wiiu/input/wiiu_hid.c | 74 ++++++++++++++++------------ wiiu/input/wiiu_hid.h | 6 +++ 5 files changed, 137 insertions(+), 52 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index 7ff4ae307b..0bda535d41 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -78,9 +78,11 @@ static void print_error(const char *fmt, int32_t errcode) RARCH_ERR(fmt, err1, err2); } -static uint32_t send_activation_packet(ds3_instance_t *instance) +static void update_pad_state(ds3_instance_t *instance); + +static int32_t send_activation_packet(ds3_instance_t *instance) { - uint32_t result; + int32_t result; #if defined(WIIU) result = HID_SET_REPORT(instance->handle, HID_REPORT_FEATURE, @@ -91,8 +93,9 @@ static uint32_t send_activation_packet(ds3_instance_t *instance) HID_SEND_CONTROL(instance->handle, activation_packet, sizeof(activation_packet)); #endif - if(result) + if(result < 0) print_error("[ds3]: activation packet failed (%d:%d)\n", result); + return result; } @@ -100,18 +103,18 @@ static uint32_t set_protocol(ds3_instance_t *instance, int protocol) { uint32_t result = 0; #if defined(WIIU) - result = HID_SET_PROTOCOL(1); + result = HID_SET_PROTOCOL(instance->handle, 1); if(result) print_error("[ds3]: set protocol failed (%d:%d)\n", result); - #endif + return result; } -static uint32_t send_control_packet(ds3_instance_t *instance) +static int32_t send_control_packet(ds3_instance_t *instance) { uint8_t packet_buffer[control_packet_size]; - uint32_t result = 0; + int32_t result = 0; memcpy(packet_buffer, control_packet, control_packet_size); packet_buffer[LED_OFFSET] = 0; @@ -131,7 +134,7 @@ static uint32_t send_control_packet(ds3_instance_t *instance) DS3_RUMBLE_REPORT_ID, packet_buffer+PACKET_OFFSET, control_packet_size-PACKET_OFFSET); - if(result) + if(result < 0) print_error("[ds3]: send control packet failed: (%d:%d)\n", result); #else HID_SEND_CONTROL(instance->handle, @@ -152,14 +155,18 @@ static void *ds3_init(void *handle) instance->handle = handle; - RARCH_LOG("[ds3]: sending activation packet\n"); - if(send_activation_packet(instance)) - errors++; +/* maybe not necessary? */ +/* RARCH_LOG("[ds3]: setting protocol\n"); if(set_protocol(instance, 1)) errors++; +*/ RARCH_LOG("[ds3]: sending control packet\n"); - if(send_control_packet(instance)) + if(send_control_packet(instance) < 0) + errors++; + + RARCH_LOG("[ds3]: sending activation packet\n"); + if(send_activation_packet(instance) < 0) errors++; if(errors) @@ -245,11 +252,10 @@ static void ds3_get_buttons(void *data, retro_bits_t *state) static void ds3_packet_handler(void *data, uint8_t *packet, uint16_t size) { ds3_instance_t *instance = (ds3_instance_t *)data; - RARCH_LOG_BUFFER(packet, size); - if(!instance->led_set) + if(instance->pad && !instance->led_set) { - send_activation_packet(instance); + send_control_packet(instance); instance->led_set = true; } @@ -261,7 +267,41 @@ static void ds3_packet_handler(void *data, uint8_t *packet, uint16_t size) } memcpy(instance->data, packet, size); + update_pad_state(instance); +} + +static void update_pad_state(ds3_instance_t *instance) +{ + uint32_t i, pressed_keys; + + static const uint32_t button_mapping[17] = + { + RETRO_DEVICE_ID_JOYPAD_SELECT, + RETRO_DEVICE_ID_JOYPAD_L3, + RETRO_DEVICE_ID_JOYPAD_R3, + RETRO_DEVICE_ID_JOYPAD_START, + RETRO_DEVICE_ID_JOYPAD_UP, + RETRO_DEVICE_ID_JOYPAD_RIGHT, + RETRO_DEVICE_ID_JOYPAD_DOWN, + RETRO_DEVICE_ID_JOYPAD_LEFT, + RETRO_DEVICE_ID_JOYPAD_L2, + RETRO_DEVICE_ID_JOYPAD_R2, + RETRO_DEVICE_ID_JOYPAD_L, + RETRO_DEVICE_ID_JOYPAD_R, + RETRO_DEVICE_ID_JOYPAD_X, + RETRO_DEVICE_ID_JOYPAD_A, + RETRO_DEVICE_ID_JOYPAD_B, + RETRO_DEVICE_ID_JOYPAD_Y, + 16 /* PS button */ + }; + instance->buttons = 0; + + pressed_keys = instance->data[2]|(instance->data[3] << 8)|((instance->data[4] & 0x01) << 16); + + for(i = 0; i < 17; i++) + instance->buttons |= (pressed_keys & (1 << i)) ? + (1 << button_mapping[i]) : 0; } static void ds3_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) @@ -272,7 +312,14 @@ static void ds3_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t static int16_t ds3_get_axis(void *data, unsigned axis) { ds3_instance_t *pad = (ds3_instance_t *)data; - return 0; + int16_t val; + + if(!pad || axis >= 4) + return 0; + + val = (pad->data[6+axis] << 8) - 0x8000; +// val = (pad->data[7+axis] << 8) - 0x8000; + return (val > 0x1000 || val < -0x1000) ? 0 : val; } static const char *ds3_get_name(void *data) @@ -284,7 +331,10 @@ static const char *ds3_get_name(void *data) static bool ds3_button(void *data, uint16_t joykey) { ds3_instance_t *pad = (ds3_instance_t *)data; - return false; + if(!pad || joykey > 31) + return false; + + return pad->buttons & (1 << joykey); } pad_connection_interface_t ds3_pad_connection = { diff --git a/input/include/hid_driver.h b/input/include/hid_driver.h index 577dbb093a..5b75c785bb 100644 --- a/input/include/hid_driver.h +++ b/input/include/hid_driver.h @@ -60,8 +60,8 @@ struct hid_driver hid_instance.os_driver_data, pad, axis) #define HID_PAD_NAME(pad) \ hid_instance.os_driver->name(hid_instance.os_driver_data, pad) -#define HID_SET_PROTOCOL(protocol) \ - hid_instance.os_driver->set_protocol(hid_instance.os_driver_data, protocol) +#define HID_SET_PROTOCOL(pad, protocol) \ + hid_instance.os_driver->set_protocol(pad, protocol) #define HID_SET_REPORT(pad, rpttype, rptid, data, len) \ hid_instance.os_driver->set_report(pad, rpttype, rptid, data, len) #define HID_SEND_CONTROL(pad, data, len) \ diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index 77a09e0487..8b37d0828d 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -250,6 +250,25 @@ DECL_AXIS_EX(r_x_minus, -3, "C-stick left") \ DECL_AXIS_EX(r_y_plus, +2, "C-stick up") \ DECL_AXIS_EX(r_y_minus, -2, "C-stick down") +#define WIIUINPUT_DS3_DEFAULT_BINDS \ +DECL_BTN_EX(menu_toggle, 16, "Playstation") \ +DECL_BTN_EX(select, 2, "Select") \ +DECL_BTN_EX(start, 3, "Start") \ +DECL_BTN_EX(a, 8, "Circle") \ +DECL_BTN_EX(y, 1, "Triangle") \ +DECL_BTN_EX(b, 0, "Cross") \ +DECL_BTN_EX(x, 9, "Square") \ +DECL_BTN_EX(r, 11, "R1") \ +DECL_BTN_EX(l, 10, "L1") \ +DECL_BTN_EX(r2, 13, "R2") \ +DECL_BTN_EX(l2, 12, "L2") \ +DECL_BTN_EX(up, 4, "D-Pad Up") \ +DECL_BTN_EX(down, 5, "D-Pad Down") \ +DECL_BTN_EX(left, 6, "D-Pad left") \ +DECL_BTN_EX(right, 7, "D-Pad Right") \ +DECL_BTN_EX(r3, 15, "R3") \ +DECL_BTN_EX(l3, 14, "L3") + #define WIIUINPUT_GAMEPAD_DEFAULT_BINDS \ DECL_BTN_EX(menu_toggle, 1, "Home") \ DECL_BTN_EX(select, 2, "-") \ @@ -635,7 +654,7 @@ const char* const input_builtin_autoconfs[] = DECL_AUTOCONF_DEVICE(PAD_NAME_CLASSIC, "wiiu", WIIUINPUT_CLASSIC_CONTROLLER_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE(PAD_NAME_HID, "wiiu", WIIUINPUT_GAMEPAD_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE("GameCube Controller", "wiiu", WIIUINPUT_GAMECUBE_DEFAULT_BINDS), - DECL_AUTOCONF_DEVICE("Sony DualShock 3", "wiiu", PS3INPUT_DEFAULT_BINDS), + DECL_AUTOCONF_DEVICE("Sony DualShock 3", "wiiu", WIIUINPUT_DS3_DEFAULT_BINDS), #endif #ifdef __CELLOS_LV2__ DECL_AUTOCONF_DEVICE("SixAxis Controller", "ps3", PS3INPUT_DEFAULT_BINDS), diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index eaa9210e68..63c9317430 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -181,14 +181,17 @@ static int32_t wiiu_hid_set_report(void *data, uint8_t report_type, uint8_t report_id, void *report_data, uint32_t report_length) { wiiu_adapter_t *adapter = (wiiu_adapter_t *)data; - if (!adapter) + if (!adapter || report_length > adapter->tx_size) return -1; + memset(adapter->tx_buffer, 0, adapter->tx_size); + memcpy(adapter->tx_buffer, report_data, report_length); + return HIDSetReport(adapter->handle, report_type, report_id, - report_data, - report_length, + adapter->tx_buffer, + adapter->tx_size, NULL, NULL); } @@ -333,10 +336,14 @@ static uint8_t try_init_driver(wiiu_adapter_t *adapter) static void synchronized_process_adapters(wiiu_hid_t *hid) { wiiu_adapter_t *adapter = NULL; + wiiu_adapter_t *prev = NULL, *adapter_next = NULL; + bool keep_prev = false; OSFastMutex_Lock(&(adapters.lock)); - for(adapter = adapters.list; adapter != NULL; adapter = adapter->next) + for(adapter = adapters.list; adapter != NULL; adapter = adapter_next) { + adapter_next = adapter->next; + switch(adapter->state) { case ADAPTER_STATE_NEW: @@ -346,10 +353,24 @@ static void synchronized_process_adapters(wiiu_hid_t *hid) case ADAPTER_STATE_READING: case ADAPTER_STATE_DONE: break; + case ADAPTER_STATE_GC: + /* remove from the list */ + if(prev == NULL) + adapters.list = adapter->next; + else + prev->next = adapter->next; + + /* adapter is no longer valid after this point */ + delete_adapter(adapter); + /* signal not to update prev ptr since adapter is now invalid */ + keep_prev = true; + break; default: RARCH_ERR("[hid]: Invalid adapter state: %d\n", adapter->state); break; } + prev = keep_prev ? prev : adapter; + keep_prev = false; } OSFastMutex_Unlock(&(adapters.lock)); } @@ -373,23 +394,15 @@ static wiiu_attach_event *synchronized_get_events_list(void) return list; } -static wiiu_adapter_t *synchronized_remove_from_adapters_list(uint32_t handle) +static wiiu_adapter_t *synchronized_lookup_adapter(uint32_t handle) { OSFastMutex_Lock(&(adapters.lock)); - wiiu_adapter_t *iterator, *prev = NULL; + wiiu_adapter_t *iterator; for(iterator = adapters.list; iterator != NULL; iterator = iterator->next) { if(iterator->handle == handle) - { - /* we're at the start of the list, so just re-assign head */ - if(prev == NULL) - adapters.list = iterator->next; - else - prev->next = iterator->next; break; - } - prev = iterator; } OSFastMutex_Unlock(&(adapters.lock)); @@ -434,12 +447,13 @@ error: static void wiiu_hid_detach(wiiu_hid_t *hid, wiiu_attach_event *event) { - wiiu_adapter_t *adapter = synchronized_remove_from_adapters_list(event->handle); + wiiu_adapter_t *adapter = synchronized_lookup_adapter(event->handle); - if(adapter) { - RARCH_LOG("[hid]: freeing detached pad\n"); - delete_adapter(adapter); - } + /* this will signal the read loop to stop for this adapter + * the read loop method will update this to ADAPTER_STATE_GC + * so the adapter poll method can clean it up. */ + if(adapter) + adapter->state = ADAPTER_STATE_DONE; } @@ -495,20 +509,21 @@ static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, adapter->state == ADAPTER_STATE_READING) { adapter->state = ADAPTER_STATE_READING; - - if(error) - { - int16_t r1 = (error & 0x0000FFFF); - int16_t r2 = ((error & 0xFFFF0000) >> 16); - RARCH_ERR("[hid]: read failed: %08x (%d:%d)\n", error, r2, r1); - } else { - adapter->driver->handle_packet(adapter->driver_handle, buffer, buffer_size); + /* "error" usually is something benign like "device not ready", at + * least from my own experiments. Just ignore the error and retry + * the read. */ + if(error == 0) { + adapter->driver->handle_packet(adapter->driver_handle, + buffer, buffer_size); } } /* this can also get set if something goes wrong in initialization */ if(adapter->state == ADAPTER_STATE_DONE) + { + adapter->state = ADAPTER_STATE_GC; return; + } HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, wiiu_hid_read_loop_callback, adapter); @@ -597,7 +612,6 @@ static void wiiu_handle_ready_adapters(wiiu_hid_t *hid) static int wiiu_hid_polling_thread(int argc, const char **argv) { wiiu_hid_t *hid = (wiiu_hid_t *)argv; - int i = 0; RARCH_LOG("[hid]: polling thread is starting\n"); @@ -606,9 +620,6 @@ static int wiiu_hid_polling_thread(int argc, const char **argv) wiiu_handle_attach_events(hid, synchronized_get_events_list()); wiiu_handle_ready_adapters(hid); usleep(10000); - i += 10000; - if(i >= (1000 * 1000 * 3)) - i = 0; } RARCH_LOG("[hid]: polling thread is stopping\n"); @@ -713,7 +724,6 @@ static wiiu_attach_event *new_attach_event(HIDDevice *device) device->vid, device->pid); return NULL; } - RARCH_LOG("[hid]: Found HID device driver: %s\n", driver->name); wiiu_attach_event *event = alloc_zeroed(4, sizeof(wiiu_attach_event)); if(!event) return NULL; diff --git a/wiiu/input/wiiu_hid.h b/wiiu/input/wiiu_hid.h index 892e1d3151..2a060d8dd2 100644 --- a/wiiu/input/wiiu_hid.h +++ b/wiiu/input/wiiu_hid.h @@ -24,10 +24,16 @@ #define DEVICE_UNUSED 0 #define DEVICE_USED 1 +/* Adapter has been detected and needs to be initialized */ #define ADAPTER_STATE_NEW 0 +/* Adapter has been initialized successfully */ #define ADAPTER_STATE_READY 1 +/* The read loop has been started */ #define ADAPTER_STATE_READING 2 +/* The read loop is shutting down */ #define ADAPTER_STATE_DONE 3 +/* The read loop has fully stopped and the adapter can be freed */ +#define ADAPTER_STATE_GC 4 struct wiiu_hid { /* used to register for HID notifications */ From de080151cc039c09e8ed2d15e77795fe37f7516f Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Fri, 6 Apr 2018 16:25:32 +0200 Subject: [PATCH 127/517] Remove excessive blend calling. --- menu/drivers/xmb.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index ca6d1d3abe..a6e788050f 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3135,9 +3135,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb->savestate_thumbnail); } - menu_display_blend_end(video_info); - menu_display_blend_begin(video_info); - /* Right thumbnail big size */ if (!settings->bools.menu_xmb_vertical_thumbnails || (settings->bools.menu_xmb_vertical_thumbnails && !xmb->left_thumbnail)) @@ -3207,9 +3204,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } } - menu_display_blend_end(video_info); - menu_display_blend_begin(video_info); - /* Left thumbnail in the left margin */ /* Do not draw the left thumbnail if there is no space available */ if (!settings->bools.menu_xmb_vertical_thumbnails && @@ -3283,9 +3277,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } } - menu_display_blend_end(video_info); - menu_display_blend_begin(video_info); - /* No Right Thumbnail, draw only the left one big size */ if (settings->bools.menu_xmb_vertical_thumbnails && !xmb->thumbnail) { @@ -3354,9 +3345,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } } - menu_display_blend_end(video_info); - menu_display_blend_begin(video_info); - /* Clock image */ menu_display_set_alpha(coord_white, MIN(xmb->alpha, 1.00f)); @@ -3482,7 +3470,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) &coord_white[0], xmb->shadow_offset); - menu_display_blend_end(video_info); menu_display_blend_begin(video_info); /* Horizontal tab icons */ @@ -3535,14 +3522,11 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } } - menu_display_blend_end(video_info); - menu_display_blend_begin(video_info); - /* Right side 2 thumbnails on top of each other */ /* here to be displayed above the horizontal icons */ - /* Do not draw the right thumbnail if there is no space available */ if (xmb->left_thumbnail && xmb->thumbnail && settings->bools.menu_xmb_vertical_thumbnails) { + /* Do not draw the right thumbnail if there is no space available */ if (((xmb->margins_screen_top + xmb->icon_size + min_thumb_size) <= height) && ((xmb->margins_screen_left * scale_mod[5] + @@ -3672,7 +3656,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } menu_display_blend_end(video_info); - menu_display_blend_begin(video_info); /* Vertical icons */ if (xmb) @@ -3701,8 +3684,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) width, height); - menu_display_blend_end(video_info); - font_driver_flush(video_info->width, video_info->height, xmb->font, video_info); font_driver_bind_block(xmb->font, NULL); From 299c808e22b34fbe258c870143d6d4e51ed3b1b1 Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Fri, 6 Apr 2018 22:47:49 +0200 Subject: [PATCH 128/517] Shorten text when 2nd thumb is on the right. --- menu/drivers/xmb.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index a6e788050f..362709187e 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -2500,6 +2500,7 @@ static int xmb_draw_item( unsigned ticker_limit = 35 * scale_mod[0]; xmb_node_t * node = (xmb_node_t*) file_list_get_userdata_at_offset(list, i); + settings_t *settings = config_get_ptr(); if (!node) goto iterate; @@ -2591,7 +2592,12 @@ static int xmb_draw_item( (!string_is_equal (thumb_ident, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) - && xmb->thumbnail) + && xmb->thumbnail) || + (!string_is_equal + (left_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) + && xmb->left_thumbnail + && settings->bools.menu_xmb_vertical_thumbnails) ) ticker_limit = 40 * scale_mod[1]; else From ee0afec78730f256021a70f8699009d41842201a Mon Sep 17 00:00:00 2001 From: Dwedit Date: Fri, 6 Apr 2018 20:08:18 -0500 Subject: [PATCH 129/517] Fix build errors on machines without HAVE_DYNAMIC --- runahead/run_ahead.c | 7 ++++++- runahead/secondary_core.c | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index a3a1cf5a87..2e039b24f6 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -203,6 +203,11 @@ void run_ahead(int runAheadCount, bool useSecondary) bool okay; bool lastFrame; bool suspendedFrame; +#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC + const bool haveDynamic = true; +#else + const bool haveDynamic = false; +#endif if (runAheadCount <= 0 || !runahead_available) { @@ -224,7 +229,7 @@ void run_ahead(int runAheadCount, bool useSecondary) runahead_check_for_gui(); - if (!useSecondary || !HAVE_DYNAMIC || !runahead_secondary_core_available) + if (!useSecondary || !haveDynamic || !runahead_secondary_core_available) { /* TODO: multiple savestates for higher performance when not using secondary core */ for (frameNumber = 0; frameNumber <= runAheadCount; frameNumber++) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 65e04b2bf9..efdd338463 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -379,6 +379,9 @@ void secondary_core_set_variable_update(void) { /* do nothing */ } - +void clear_controller_port_map(void) +{ + /* do nothing */ +} #endif From cb74af60c9efccc90a3cbf1e4ee1e490e90903f2 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Fri, 6 Apr 2018 20:14:29 -0500 Subject: [PATCH 130/517] one line typo --- runahead/run_ahead.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index 2e039b24f6..e03eb23571 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -203,7 +203,7 @@ void run_ahead(int runAheadCount, bool useSecondary) bool okay; bool lastFrame; bool suspendedFrame; -#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC +#if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB) const bool haveDynamic = true; #else const bool haveDynamic = false; From f069eeb017d7848070d64a589e841184fc919db5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 00:48:27 +0200 Subject: [PATCH 131/517] (Wii/WiiU) Add HAVE_RUNAHEAD --- Makefile.griffin | 5 +++++ Makefile.wiiu | 1 + 2 files changed, 6 insertions(+) diff --git a/Makefile.griffin b/Makefile.griffin index 0f890d36c3..1e8b848e82 100644 --- a/Makefile.griffin +++ b/Makefile.griffin @@ -178,6 +178,7 @@ else ifeq ($(libogc_platform), 1) CFLAGS += -DGEKKO -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int + HAVE_RUNAHEAD := 1 HAVE_FILTERS_BUILTIN := 1 HAVE_THREADS := 1 HAVE_RPNG := 1 @@ -840,6 +841,10 @@ ifeq ($(HAVE_IMAGEVIEWER), 1) CFLAGS += -DHAVE_IMAGEVIEWER endif +ifeq ($(HAVE_RUNAHEAD), 1) + CFLAGS += -DHAVE_RUNAHEAD +endif + ifeq ($(HAVE_7ZIP), 1) CFLAGS += -DHAVE_7ZIP endif diff --git a/Makefile.wiiu b/Makefile.wiiu index 4b5eeafc02..0f512f5638 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -83,6 +83,7 @@ ifeq ($(SALAMANDER_BUILD),1) else DEFINES += -DRARCH_INTERNAL DEFINES += -DHAVE_KEYMAPPER + DEFINES += -DHAVE_RUNAHEAD DEFINES += -DHAVE_UPDATE_ASSETS DEFINES += -DHAVE_FILTERS_BUILTIN DEFINES += -DHAVE_SLANG From 360bea85b9d351812347d79be5ba3bf6d095b8ce Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 01:09:31 +0200 Subject: [PATCH 132/517] (PS3) Silence more warnings --- Makefile.ps3 | 2 +- dynamic.c | 30 ++++++++++--------- libretro-common/net/net_http.c | 1 - runahead/run_ahead.c | 53 +++++++++++++++++++--------------- runahead/secondary_core.c | 8 ++--- 5 files changed, 49 insertions(+), 45 deletions(-) diff --git a/Makefile.ps3 b/Makefile.ps3 index e4c96fca7a..f3f29d18de 100644 --- a/Makefile.ps3 +++ b/Makefile.ps3 @@ -63,7 +63,7 @@ endif PPU_SRCS = griffin/griffin.c -DEFINES += -DHAVE_MENU -DHAVE_RGUI -DHAVE_XMB -DHAVE_LIBRETRODB -DHAVE_MATERIALUI -DHAVE_SHADERPIPELINE -DRARCH_INTERNAL -DMSB_FIRST -DHAVE_OVERLAY -DHAVE_CC_RESAMPLER -DHAVE_STB_VORBIS -DHAVE_STB_FONT +DEFINES += -DHAVE_MENU -DHAVE_RGUI -DHAVE_XMB -DHAVE_LIBRETRODB -DHAVE_MATERIALUI -DHAVE_SHADERPIPELINE -DRARCH_INTERNAL -DMSB_FIRST -DHAVE_OVERLAY -DHAVE_CC_RESAMPLER -DHAVE_STB_VORBIS -DHAVE_STB_FONT -DHAVE_RUNAHEAD ifeq ($(DEX_BUILD), 1) DEFINES += -DDEX_BUILD diff --git a/dynamic.c b/dynamic.c index 972b85b97b..4795c22011 100644 --- a/dynamic.c +++ b/dynamic.c @@ -922,25 +922,27 @@ static bool dynamic_request_hw_context(enum retro_hw_context_type type, break; #endif - case RETRO_HW_CONTEXT_DIRECT3D: - switch(major) - { +#if defined(HAVE_D3D9) || defined(HAVE_D3D11) + case RETRO_HW_CONTEXT_DIRECT3D: + switch (major) + { #ifdef HAVE_D3D9 - case 9: - RARCH_LOG("Requesting D3D9 context.\n"); - break; + case 9: + RARCH_LOG("Requesting D3D9 context.\n"); + break; #endif #ifdef HAVE_D3D11 - case 11: - RARCH_LOG("Requesting D3D11 context.\n"); - break; + case 11: + RARCH_LOG("Requesting D3D11 context.\n"); + break; +#endif + default: + RARCH_LOG("Requesting unknown context.\n"); + return false; + } + break; #endif - default: - goto unknown; - } - break; -unknown: default: RARCH_LOG("Requesting unknown context.\n"); return false; diff --git a/libretro-common/net/net_http.c b/libretro-common/net/net_http.c index 3b25f340d9..caa6e56c93 100644 --- a/libretro-common/net/net_http.c +++ b/libretro-common/net/net_http.c @@ -138,7 +138,6 @@ void net_http_urlencode_full(char *dest, char *tmp = NULL; char url_domain[PATH_MAX_LENGTH] = {0}; char url_path[PATH_MAX_LENGTH] = {0}; - char url_encoded[PATH_MAX_LENGTH] = {0}; int count = 0; strlcpy (url_path, source, sizeof(url_path)); diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index e03eb23571..d8111ff1fb 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -28,6 +28,7 @@ static void unset_fast_savestate(void); static void set_hard_disable_audio(void); static void unset_hard_disable_audio(void); +/* TODO/FIXME - shouldn't this be signed size_t? */ static size_t runahead_save_state_size = -1; /* Save State List for Run Ahead */ @@ -197,19 +198,18 @@ static void runahead_check_for_gui(void) runahead_last_frame_count = frame_count; } -void run_ahead(int runAheadCount, bool useSecondary) +void run_ahead(int runahead_count, bool useSecondary) { - int frameNumber; - bool okay; - bool lastFrame; - bool suspendedFrame; + int frame_number = 0; + bool last_frame = false; + bool suspended_frame = false; #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB) - const bool haveDynamic = true; + const bool have_dynamic = true; #else - const bool haveDynamic = false; + const bool have_dynamic = false; #endif - if (runAheadCount <= 0 || !runahead_available) + if (runahead_count <= 0 || !runahead_available) { core_run(); runahead_force_input_dirty = true; @@ -220,7 +220,8 @@ void run_ahead(int runAheadCount, bool useSecondary) { if (!runahead_create()) { - /* RunAhead has been disabled because the core does not support savestates. */ + /* RunAhead has been disabled because the core + * does not support savestates. */ core_run(); runahead_force_input_dirty = true; return; @@ -229,41 +230,44 @@ void run_ahead(int runAheadCount, bool useSecondary) runahead_check_for_gui(); - if (!useSecondary || !haveDynamic || !runahead_secondary_core_available) + if (!useSecondary || !have_dynamic || !runahead_secondary_core_available) { - /* TODO: multiple savestates for higher performance when not using secondary core */ - for (frameNumber = 0; frameNumber <= runAheadCount; frameNumber++) + /* TODO: multiple savestates for higher performance + * when not using secondary core */ + for (frame_number = 0; frame_number <= runahead_count; frame_number++) { - lastFrame = frameNumber == runAheadCount; - suspendedFrame = !lastFrame; + last_frame = frame_number == runahead_count; + suspended_frame = !last_frame; - if (suspendedFrame) + if (suspended_frame) { runahead_suspend_audio(); runahead_suspend_video(); } - if (frameNumber == 0) + if (frame_number == 0) core_run(); else core_run_no_input_polling(); - if (suspendedFrame) + if (suspended_frame) { runahead_resume_video(); runahead_resume_audio(); } - if (frameNumber == 0) + if (frame_number == 0) { - /* RunAhead has been disabled due to save state failure */ + /* RunAhead has been disabled due + * to save state failure */ if (!runahead_save_state()) return; } - if (lastFrame) + if (last_frame) { - /* RunAhead has been disabled due to load state failure */ + /* RunAhead has been disabled due + * to load state failure */ if (!runahead_load_state()) return; } @@ -272,6 +276,8 @@ void run_ahead(int runAheadCount, bool useSecondary) else { #if HAVE_DYNAMIC + bool okay = false; + /* run main core with video suspended */ runahead_suspend_video(); core_run(); @@ -291,7 +297,8 @@ void run_ahead(int runAheadCount, bool useSecondary) if (!runahead_load_state_secondary()) return; - for (frame_count = 0; frame_count < (unsigned)(runAheadCount - 1); frame_count++) + for (frame_count = 0; frame_count < + (unsigned)(runahead_count - 1); frame_count++) { runahead_suspend_video(); runahead_suspend_audio(); @@ -353,9 +360,9 @@ static bool runahead_create(void) static bool runahead_save_state(void) { + bool okay = false; retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; - bool okay; set_fast_savestate(); okay = core_serialize(serialize_info); unset_fast_savestate(); diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index efdd338463..768d29c5de 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -87,7 +87,6 @@ char* get_temp_directory_alloc(void) char* copy_core_to_temp_file(void) { - bool okay = false; char *tempDirectory = NULL; char *retroarchTempPath = NULL; char *tempDllPath = NULL; @@ -146,7 +145,6 @@ bool write_file_with_random_name(char **tempDllPath, { unsigned i; char numberBuf[32]; - bool okay = false; const char *prefix = "tmp"; time_t timeValue = time(NULL); unsigned int numberValue = (unsigned int)timeValue; @@ -298,8 +296,7 @@ bool secondary_core_run_no_input_polling(void) { if (!secondary_module) { - bool okay = secondary_core_create(); - if (!okay) + if (!secondary_core_create()) return false; } secondary_core.retro_run(); @@ -310,8 +307,7 @@ bool secondary_core_deserialize(const void *buffer, int size) { if (!secondary_module) { - bool okay = secondary_core_create(); - if (!okay) + if (!secondary_core_create()) { secondary_core_destroy(); return false; From 9e07e1a57fe5ee891452fbcf242d8e46e906ea18 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 01:20:55 +0200 Subject: [PATCH 133/517] (runahead) Buildfix --- runahead/secondary_core.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index 768d29c5de..f2f914edf0 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -107,9 +107,7 @@ char* copy_core_to_temp_file(void) strcat_alloc(&retroarchTempPath, "retroarch_temp"); strcat_alloc(&retroarchTempPath, path_default_slash()); - okay = path_mkdir(retroarchTempPath); - - if (!okay) + if (!path_mkdir(retroarchTempPath)) goto failed; if (!filestream_read_file(corePath, &dllFileData, &dllFileSize)) @@ -117,13 +115,11 @@ char* copy_core_to_temp_file(void) strcat_alloc(&tempDllPath, retroarchTempPath); strcat_alloc(&tempDllPath, coreBaseName); - okay = filestream_write_file(tempDllPath, dllFileData, dllFileSize); - if (!okay) + if (!filestream_write_file(tempDllPath, dllFileData, dllFileSize)) { /* try other file names */ - okay = write_file_with_random_name(&tempDllPath, retroarchTempPath, dllFileData, dllFileSize); - if (!okay) + if (!write_file_with_random_name(&tempDllPath, retroarchTempPath, dllFileData, dllFileSize)) goto failed; } @@ -144,35 +140,36 @@ bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, ssize_t dataSize) { unsigned i; - char numberBuf[32]; + char number_buf[32]; const char *prefix = "tmp"; - time_t timeValue = time(NULL); - unsigned int numberValue = (unsigned int)timeValue; + time_t time_value = time(NULL); + unsigned int number_value= (unsigned int)time_value; int number = 0; char *ext = strcpy_alloc_force(path_get_extension(*tempDllPath)); - int extLen = strlen(ext); + int ext_len = strlen(ext); - if (extLen > 0) + if (ext_len > 0) { strcat_alloc(&ext, "."); - memmove(ext + 1, ext, extLen); + memmove(ext + 1, ext, ext_len); ext[0] = '.'; - extLen++; + ext_len++; } - /* try up to 30 'random' filenames before giving up */ + /* Try up to 30 'random' filenames before giving up */ for (i = 0; i < 30; i++) { - numberValue = numberValue * 214013 + 2531011; - number = (numberValue >> 14) % 100000; - sprintf(numberBuf, "%05d", number); + number_value = number_value * 214013 + 2531011; + number = (number_value >> 14) % 100000; + + snprintf(number_buf, sizeof(number_buf), "%05d", number); + FREE(*tempDllPath); strcat_alloc(tempDllPath, retroarchTempPath); strcat_alloc(tempDllPath, prefix); - strcat_alloc(tempDllPath, numberBuf); + strcat_alloc(tempDllPath, number_buf); strcat_alloc(tempDllPath, ext); - okay = filestream_write_file(*tempDllPath, data, dataSize); - if (okay) + if (filestream_write_file(*tempDllPath, data, dataSize)) break; } From 8e4df273055d2c42b3ec56e621f8aa298f1d67a5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 01:28:17 +0200 Subject: [PATCH 134/517] (runahead) Cleanups --- runahead/run_ahead.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index d8111ff1fb..f59ddde051 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -367,22 +367,28 @@ static bool runahead_save_state(void) okay = core_serialize(serialize_info); unset_fast_savestate(); if (!okay) + { runahead_error(); - return okay; + return false; + } + return true; } static bool runahead_load_state(void) { + bool okay = false; retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*) runahead_save_state_list->data[0]; - bool lastDirty = input_is_dirty; - bool okay; + bool last_dirty = input_is_dirty; + set_fast_savestate(); - /* calling core_unserialize has side effects with netplay (it triggers transmitting your save state) + /* calling core_unserialize has side effects with + * netplay (it triggers transmitting your save state) call retro_unserialize directly from the core instead */ - okay = current_core.retro_unserialize(serialize_info->data_const, serialize_info->size); + okay = current_core.retro_unserialize( + serialize_info->data_const, serialize_info->size); unset_fast_savestate(); - input_is_dirty = lastDirty; + input_is_dirty = last_dirty; if (!okay) runahead_error(); @@ -392,25 +398,32 @@ static bool runahead_load_state(void) static bool runahead_load_state_secondary(void) { + bool okay = false; retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0]; - bool okay; + set_fast_savestate(); - okay = secondary_core_deserialize(serialize_info->data_const, serialize_info->size); + okay = secondary_core_deserialize( + serialize_info->data_const, serialize_info->size); unset_fast_savestate(); if (!okay) + { runahead_secondary_core_available = false; + return false; + } - return okay; + return true; } static bool runahead_run_secondary(void) { - bool okay = secondary_core_run_no_input_polling(); - if (!okay) + if (!secondary_core_run_no_input_polling()) + { runahead_secondary_core_available = false; - return okay; + return false; + } + return true; } static void runahead_suspend_audio(void) From 4f32eb9cc192ce0e0d4c3e52e25f6d07cb9972b5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 01:36:30 +0200 Subject: [PATCH 135/517] (runahead) Style nits - no more '== NULL' --- runahead/copy_load_info.c | 31 ++++++++----- runahead/dirty_input.c | 92 ++++++++++++++++++++++----------------- 2 files changed, 72 insertions(+), 51 deletions(-) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c index b9b6aa590b..86c92423c4 100644 --- a/runahead/copy_load_info.c +++ b/runahead/copy_load_info.c @@ -12,7 +12,7 @@ enum rarch_core_type last_core_type; static void free_retro_game_info(struct retro_game_info *dest) { - if (dest == NULL) + if (!dest) return; FREE(dest->path); FREE(dest->data); @@ -22,15 +22,19 @@ static void free_retro_game_info(struct retro_game_info *dest) static struct retro_game_info* clone_retro_game_info(const struct retro_game_info *src) { - struct retro_game_info *dest; - if (src == NULL) + struct retro_game_info *dest = NULL; + if (!src) return NULL; dest = (struct retro_game_info*)calloc(1, sizeof(struct retro_game_info)); + if (!dest) + return NULL; + dest->path = strcpy_alloc(src->path); dest->data = memcpy_alloc(src->data, src->size); dest->size = src->size; dest->meta = strcpy_alloc(src->meta); + return dest; } @@ -55,10 +59,12 @@ static struct string_list* clone_string_list(const struct string_list *src) if (!src) return NULL; - dest = (struct string_list*)calloc(1, sizeof(struct string_list)); + dest = (struct string_list*) + calloc(1, sizeof(struct string_list)); dest->size = src->size; dest->cap = src->cap; - dest->elems = (struct string_list_elem*)calloc(dest->size, sizeof(struct string_list_elem)); + dest->elems = (struct string_list_elem*) + calloc(dest->size, sizeof(struct string_list_elem)); for (i = 0; i < src->size; i++) { @@ -74,7 +80,8 @@ static struct string_list* clone_string_list(const struct string_list *src) static void free_retro_subsystem_memory_info(struct retro_subsystem_memory_info *dest) { - if (dest == NULL) return; + if (!dest) + return; FREE(dest->extension); } @@ -90,7 +97,7 @@ static void free_retro_subsystem_rom_info(struct retro_subsystem_rom_info *dest) { int i; - if (dest == NULL) + if (!dest) return; FREE(dest->desc); @@ -130,7 +137,7 @@ static void free_retro_subsystem_info(struct retro_subsystem_info *dest) { int i; - if (dest == NULL) + if (!dest) return; FREE(dest->desc); @@ -150,7 +157,7 @@ static retro_subsystem_info* clone_retro_subsystem_info(struct retro_subsystem_info *dest = NULL; retro_subsystem_rom_info *roms = NULL; - if (src == NULL) + if (!src) return NULL; dest = (struct retro_subsystem_info*)calloc(1, sizeof(struct retro_subsystem_info)); @@ -172,7 +179,7 @@ static retro_subsystem_info* clone_retro_subsystem_info(struct static void free_retro_ctx_load_content_info(struct retro_ctx_load_content_info *dest) { - if (dest == NULL) + if (!dest) return; free_retro_game_info(dest->info); @@ -190,8 +197,8 @@ static struct retro_ctx_load_content_info *clone_retro_ctx_load_content_info( const struct retro_ctx_load_content_info *src) { - struct retro_ctx_load_content_info *dest; - if (src == NULL || src->special != NULL) + struct retro_ctx_load_content_info *dest = NULL; + if (!src || src->special != NULL) return NULL; /* refuse to deal with the Special field */ dest = (struct retro_ctx_load_content_info*)calloc(1, diff --git a/runahead/dirty_input.c b/runahead/dirty_input.c index 606f40e892..5330200de2 100644 --- a/runahead/dirty_input.c +++ b/runahead/dirty_input.c @@ -8,8 +8,8 @@ #include "mylist.h" #include "mem_util.h" -bool input_is_dirty; -static MyList *inputStateList; +bool input_is_dirty = false; +static MyList *input_state_list = NULL; typedef struct InputListElement_t { @@ -41,22 +41,23 @@ static void* InputListElementConstructor(void) static void input_state_destory(void) { - mylist_destroy(&inputStateList); + mylist_destroy(&input_state_list); } -static void input_state_setlast(unsigned port, unsigned device, +static void input_state_set_last(unsigned port, unsigned device, unsigned index, unsigned id, int16_t value) { - int i; - InputListElement *element; + unsigned i; + InputListElement *element = NULL; - if (!inputStateList) - mylist_create(&inputStateList, 16, InputListElementConstructor, free); + if (!input_state_list) + mylist_create(&input_state_list, 16, + InputListElementConstructor, free); /* find list item */ - for (i = 0; i < inputStateList->size; i++) + for (i = 0; i < input_state_list->size; i++) { - element = (InputListElement*)inputStateList->data[i]; + element = (InputListElement*)input_state_list->data[i]; if ( element->port == port && element->device == device && element->index == index) { @@ -64,40 +65,49 @@ static void input_state_setlast(unsigned port, unsigned device, return; } } - element = (InputListElement*)mylist_add_element(inputStateList); - element->port = port; - element->device = device; - element->index = index; + + element = (InputListElement*) + mylist_add_element(input_state_list); + element->port = port; + element->device = device; + element->index = index; element->state[id] = value; } -static int16_t input_state_getlast(unsigned port, unsigned device, unsigned index, unsigned id) +static int16_t input_state_get_last(unsigned port, + unsigned device, unsigned index, unsigned id) { - int i; - InputListElement *element = NULL; + unsigned i; - if (!inputStateList) + if (!input_state_list) return 0; /* find list item */ - for (i = 0; i < inputStateList->size; i++) + for (i = 0; i < input_state_list->size; i++) { - element = (InputListElement*)inputStateList->data[i]; - if (element->port == port && element->device == device && element->index == index) + InputListElement *element = + (InputListElement*)input_state_list->data[i]; + + if ( (element->port == port) && + (element->device == device) && + (element->index == index) + ) return element->state[id]; } return 0; } -static int16_t input_state_with_logging(unsigned port, unsigned device, unsigned index, unsigned id) +static int16_t input_state_with_logging(unsigned port, + unsigned device, unsigned index, unsigned id) { - if (input_state_callback_original != NULL) + if (input_state_callback_original) { - int16_t result = input_state_callback_original(port, device, index, id); - int16_t lastInput = input_state_getlast(port, device, index, id); - if (result != lastInput) + int16_t result = input_state_callback_original( + port, device, index, id); + int16_t last_input = input_state_get_last(port, device, index, id); + if (result != last_input) input_is_dirty = true; - input_state_setlast(port, device, index, id, result); + input_state_set_last(port, device, index, id, result); return result; } return 0; @@ -120,41 +130,45 @@ static bool unserialze_hook(const void *buf, size_t size) void add_input_state_hook(void) { - if (input_state_callback_original == NULL) + if (!input_state_callback_original) { input_state_callback_original = retro_ctx.state_cb; - retro_ctx.state_cb = input_state_with_logging; + retro_ctx.state_cb = input_state_with_logging; current_core.retro_set_input_state(retro_ctx.state_cb); } - if (retro_reset_callback_original == NULL) + + if (!retro_reset_callback_original) { retro_reset_callback_original = current_core.retro_reset; - current_core.retro_reset = reset_hook; + current_core.retro_reset = reset_hook; } - if (retro_unserialize_callback_original == NULL) + + if (!retro_unserialize_callback_original) { retro_unserialize_callback_original = current_core.retro_unserialize; - current_core.retro_unserialize = unserialze_hook; + current_core.retro_unserialize = unserialze_hook; } } void remove_input_state_hook(void) { - if (input_state_callback_original != NULL) + if (input_state_callback_original) { - retro_ctx.state_cb = input_state_callback_original; + retro_ctx.state_cb = input_state_callback_original; current_core.retro_set_input_state(retro_ctx.state_cb); input_state_callback_original = NULL; input_state_destory(); } - if (retro_reset_callback_original != NULL) + + if (retro_reset_callback_original) { - current_core.retro_reset = retro_reset_callback_original; + current_core.retro_reset = retro_reset_callback_original; retro_reset_callback_original = NULL; } - if (retro_unserialize_callback_original != NULL) + + if (retro_unserialize_callback_original) { - current_core.retro_unserialize = retro_unserialize_callback_original; + current_core.retro_unserialize = retro_unserialize_callback_original; retro_unserialize_callback_original = NULL; } } From 2ca52bd08c88b1021564cbcab04eba58e3cd5ac1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 01:41:41 +0200 Subject: [PATCH 136/517] Remove memcpy_alloc --- runahead/copy_load_info.c | 19 +++++++++++++++---- runahead/mem_util.c | 7 ------- runahead/mem_util.h | 1 - 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c index 86c92423c4..83824501b2 100644 --- a/runahead/copy_load_info.c +++ b/runahead/copy_load_info.c @@ -22,18 +22,29 @@ static void free_retro_game_info(struct retro_game_info *dest) static struct retro_game_info* clone_retro_game_info(const struct retro_game_info *src) { + void *data = NULL; struct retro_game_info *dest = NULL; + if (!src) return NULL; + dest = (struct retro_game_info*)calloc(1, sizeof(struct retro_game_info)); if (!dest) return NULL; - dest->path = strcpy_alloc(src->path); - dest->data = memcpy_alloc(src->data, src->size); - dest->size = src->size; - dest->meta = strcpy_alloc(src->meta); + dest->data = NULL; + dest->path = strcpy_alloc(src->path); + + data = malloc(src->size); + + if (data) + { + memcpy(data, src->data, src->size); + dest->data = data; + } + dest->size = src->size; + dest->meta = strcpy_alloc(src->meta); return dest; } diff --git a/runahead/mem_util.c b/runahead/mem_util.c index 1657be4484..d04e3097d9 100644 --- a/runahead/mem_util.c +++ b/runahead/mem_util.c @@ -2,13 +2,6 @@ #include "mem_util.h" -void *memcpy_alloc(const void *src, size_t size) -{ - void *result = malloc(size); - memcpy(result, src, size); - return result; -} - char *strcpy_alloc(const char *sourceStr) { size_t len = 0; diff --git a/runahead/mem_util.h b/runahead/mem_util.h index 3b5c1a41b6..aedcea23d3 100644 --- a/runahead/mem_util.h +++ b/runahead/mem_util.h @@ -14,7 +14,6 @@ RETRO_BEGIN_DECLS char *strcpy_alloc(const char *sourceStr); char *strcpy_alloc_force(const char *sourceStr); void strcat_alloc(char ** destStr_p, const char *appendStr); -void *memcpy_alloc(const void *src, size_t size); RETRO_END_DECLS From d58c425e43f078599cfa155cf7d28bd0a87dbe2c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 01:56:06 +0200 Subject: [PATCH 137/517] Cleanups --- runahead/copy_load_info.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c index 83824501b2..7e5d9dd41b 100644 --- a/runahead/copy_load_info.c +++ b/runahead/copy_load_info.c @@ -62,26 +62,41 @@ static void free_string_list(struct string_list *dest) FREE(dest->elems); } -static struct string_list* clone_string_list(const struct string_list *src) +static struct string_list *clone_string_list(const struct string_list *src) { unsigned i; - struct string_list *dest = NULL; + struct string_list_elem *elems = NULL; + struct string_list *dest = NULL; if (!src) return NULL; - dest = (struct string_list*) + dest = (struct string_list*) calloc(1, sizeof(struct string_list)); - dest->size = src->size; - dest->cap = src->cap; - dest->elems = (struct string_list_elem*) + + if (!dest) + return NULL; + + dest->size = src->size; + dest->cap = src->cap; + + elems = (struct string_list_elem*) calloc(dest->size, sizeof(struct string_list_elem)); + if (!elems) + { + free(dest); + return NULL; + } + + dest->elems = elems; + for (i = 0; i < src->size; i++) { dest->elems[i].data = strcpy_alloc(src->elems[i].data); dest->elems[i].attr = src->elems[i].attr; } + return dest; } From c570d461afc9ce734bbe7b0e916abc3b297fd234 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 02:01:37 +0200 Subject: [PATCH 138/517] Cleanups --- runahead/copy_load_info.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c index 7e5d9dd41b..e78c6238ad 100644 --- a/runahead/copy_load_info.c +++ b/runahead/copy_load_info.c @@ -43,6 +43,7 @@ static struct retro_game_info* clone_retro_game_info(const memcpy(data, src->data, src->size); dest->data = data; } + dest->size = src->size; dest->meta = strcpy_alloc(src->meta); @@ -62,16 +63,12 @@ static void free_string_list(struct string_list *dest) FREE(dest->elems); } -static struct string_list *clone_string_list(const struct string_list *src) +static struct string_list *string_list_clone( + const struct string_list *src) { unsigned i; struct string_list_elem *elems = NULL; - struct string_list *dest = NULL; - - if (!src) - return NULL; - - dest = (struct string_list*) + struct string_list *dest = (struct string_list*) calloc(1, sizeof(struct string_list)); if (!dest) @@ -227,13 +224,20 @@ static struct retro_ctx_load_content_info if (!src || src->special != NULL) return NULL; /* refuse to deal with the Special field */ - dest = (struct retro_ctx_load_content_info*)calloc(1, - sizeof(struct retro_ctx_load_content_info)); - dest->info = clone_retro_game_info(src->info); - dest->content = clone_string_list(src->content); - dest->special = NULL; + dest = (struct retro_ctx_load_content_info*) + calloc(1, sizeof(*dest)); + + if (!dest) + return NULL; + + dest->info = clone_retro_game_info(src->info); + dest->content = NULL; + dest->special = NULL; + + if (src->content) + dest->content = string_list_clone(src->content); #if 0 - dest->special = clone_retro_subsystem_info(src->special); + dest->special = clone_retro_subsystem_info(src->special); #endif return dest; } From 23096e00a3cc9e735e0f5c1ec684dbbde6ace880 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 07:15:59 +0200 Subject: [PATCH 139/517] (wiiu) Add HAVE_RUNAHEAD object files --- Makefile.wiiu | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile.wiiu b/Makefile.wiiu index 0f512f5638..4a9f3bc675 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -6,6 +6,7 @@ DEBUG = 0 GRIFFIN_BUILD = 0 SALAMANDER_BUILD = 0 WHOLE_ARCHIVE_LINK = 0 +HAVE_RUNAHEAD = 1 WIIU_HID = 0 WIIU_LOG_RPX = 0 BUILD_DIR = objs/wiiu @@ -83,12 +84,21 @@ ifeq ($(SALAMANDER_BUILD),1) else DEFINES += -DRARCH_INTERNAL DEFINES += -DHAVE_KEYMAPPER - DEFINES += -DHAVE_RUNAHEAD DEFINES += -DHAVE_UPDATE_ASSETS DEFINES += -DHAVE_FILTERS_BUILTIN DEFINES += -DHAVE_SLANG DEFINES += -DHAVE_SHADERPIPELINE +ifeq ($(HAVE_RUNAHEAD),1) + DEFINES += -DHAVE_RUNAHEAD + OBJ += runahead/copy_load_info.o + OBJ += runahead/dirty_input.o + OBJ += runahead/mem_util.o + OBJ += runahead/mylist.o + OBJ += runahead/run_ahead.o + OBJ += runahead/secondary_core.o +endif + OBJ += wiiu/system/missing_libc_functions.o OBJ += wiiu/shader_utils.o OBJ += gfx/drivers/gx2_shaders/tex.o From 63c99363bd8da77dce7ce8273d6c5e7e4cbd2261 Mon Sep 17 00:00:00 2001 From: orbea Date: Sun, 8 Apr 2018 08:01:26 -0700 Subject: [PATCH 140/517] Hide the 'Core delete' option if the 'Core updater' is also hidden. --- menu/menu_displaylist.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 66f3756e96..ef80c047b4 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -153,6 +153,7 @@ static int menu_displaylist_parse_core_info(menu_displaylist_info_t *info) unsigned i; char tmp[PATH_MAX_LENGTH]; core_info_t *core_info = NULL; + settings_t *settings = config_get_ptr(); tmp[0] = '\0'; @@ -336,11 +337,12 @@ static int menu_displaylist_parse_core_info(menu_displaylist_info_t *info) } } - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_DELETE), - msg_hash_to_str(MENU_ENUM_LABEL_CORE_DELETE), - MENU_ENUM_LABEL_CORE_DELETE, - MENU_SETTING_ACTION_CORE_DELETE, 0, 0); + if (settings->bools.menu_show_core_updater) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_DELETE), + msg_hash_to_str(MENU_ENUM_LABEL_CORE_DELETE), + MENU_ENUM_LABEL_CORE_DELETE, + MENU_SETTING_ACTION_CORE_DELETE, 0, 0); return 0; } From 816d805f5888b01b62dce2be69ce232eb1b6ab2b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 17:26:34 +0200 Subject: [PATCH 141/517] (wiiu) Only build these files in for non-Griffin build --- Makefile.wiiu | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.wiiu b/Makefile.wiiu index 4a9f3bc675..5b2d3625a9 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -91,12 +91,14 @@ else ifeq ($(HAVE_RUNAHEAD),1) DEFINES += -DHAVE_RUNAHEAD +ifneq ($(GRIFFIN_BUILD), 1) OBJ += runahead/copy_load_info.o OBJ += runahead/dirty_input.o OBJ += runahead/mem_util.o OBJ += runahead/mylist.o OBJ += runahead/run_ahead.o OBJ += runahead/secondary_core.o +endif endif OBJ += wiiu/system/missing_libc_functions.o From 9639389a66b6577b1da11a7af7eb5cdf65984d77 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 17:58:10 +0200 Subject: [PATCH 142/517] Uniquely name crc32.c to avoid collissions with statically linked builds --- Makefile.common | 2 +- deps/libz/{crc32.c => libz-crc32.c} | 0 griffin/griffin.c | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename deps/libz/{crc32.c => libz-crc32.c} (100%) diff --git a/Makefile.common b/Makefile.common index 972d61e34e..2e0d9b61ae 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1472,7 +1472,7 @@ ifeq ($(HAVE_ZLIB), 1) ifeq ($(HAVE_BUILTINZLIB), 1) OBJ += $(DEPS_DIR)/libz/adler32.o \ $(DEPS_DIR)/libz/compress.o \ - $(DEPS_DIR)/libz/crc32.o \ + $(DEPS_DIR)/libz/libz-crc32.o \ $(DEPS_DIR)/libz/deflate.o \ $(DEPS_DIR)/libz/gzclose.o \ $(DEPS_DIR)/libz/gzlib.o \ diff --git a/deps/libz/crc32.c b/deps/libz/libz-crc32.c similarity index 100% rename from deps/libz/crc32.c rename to deps/libz/libz-crc32.c diff --git a/griffin/griffin.c b/griffin/griffin.c index bb0d9121f6..e92b9da779 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1267,7 +1267,7 @@ DEPENDENCIES #ifdef WANT_ZLIB #include "../deps/libz/adler32.c" #include "../deps/libz/compress.c" -#include "../deps/libz/crc32.c" +#include "../deps/libz/libz-crc32.c" #include "../deps/libz/deflate.c" #include "../deps/libz/gzclose.c" #include "../deps/libz/gzlib.c" From ed334cd1ddbedcee0f0c7e9e76fe0e1b003d2115 Mon Sep 17 00:00:00 2001 From: radius Date: Wed, 28 Mar 2018 17:45:05 -0500 Subject: [PATCH 143/517] remap-redux part 2: allow multiple gamepads to work for the keymapper --- Makefile.vita | 3 ++- configuration.h | 1 + input/input_mapper.c | 40 +++++++++++++++++++++++------------ input/input_remapping.c | 5 +++++ menu/cbs/menu_cbs_get_value.c | 11 ++++++++-- menu/menu_displaylist.c | 9 ++++---- menu/menu_driver.h | 2 +- 7 files changed, 49 insertions(+), 22 deletions(-) diff --git a/Makefile.vita b/Makefile.vita index 25d79a673e..5e8f135011 100644 --- a/Makefile.vita +++ b/Makefile.vita @@ -16,7 +16,7 @@ DEFINES := ifeq ($(GRIFFIN_BUILD), 1) OBJ += griffin/griffin.o DEFINES += -DHAVE_GRIFFIN=1 - DEFINES += -DHAVE_NEON -DHAVE_MENU -DHAVE_XMB -DHAVE_MATERIALUI -DHAVE_LIBRETRODB + DEFINES += -DHAVE_NEON -DHAVE_MENU -DHAVE_XMB -DHAVE_MATERIALUI -DHAVE_LIBRETRODB DEFINES -DHAVE_KEYMAPPER DEFINES += -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DWANT_ZLIB -DHAVE_CC_RESAMPLER ifeq ($(DEBUG), 1) DEFINES += -DHAVE_NETLOGGER @@ -33,6 +33,7 @@ else HAVE_ZLIB := 1 HAVE_7ZIP := 1 HAVE_VITA2D := 1 + HAVE_KEYMAPPER := 1 HAVE_NETWORKING := 1 HAVE_SOCKET_LEGACY := 1 HAVE_MENU := 1 diff --git a/configuration.h b/configuration.h index b43facf5f6..30eaf38b9c 100644 --- a/configuration.h +++ b/configuration.h @@ -380,6 +380,7 @@ typedef struct settings unsigned input_analog_dpad_mode[MAX_USERS]; unsigned input_keymapper_ids[RARCH_CUSTOM_BIND_LIST_END]; + unsigned input_keymapper_multi_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END]; unsigned input_remap_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END]; diff --git a/input/input_mapper.c b/input/input_mapper.c index 2b58e9b701..1bc5c5c6f2 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -47,6 +47,7 @@ #define MAPPER_GET_KEY(state, key) (((state)->keys[(key) / 32] >> ((key) % 32)) & 1) #define MAPPER_SET_KEY(state, key) (state)->keys[(key) / 32] |= 1 << ((key) % 32) +#define MAPPER_UNSET_KEY(state, key) (state)->keys[(key) / 32] |= 0 << ((key) % 32) struct input_mapper { @@ -82,9 +83,10 @@ void input_mapper_free(input_mapper_t *handle) void input_mapper_poll(input_mapper_t *handle) { - int i; + int i, j; settings_t *settings = config_get_ptr(); unsigned device = settings->uints.input_libretro_device[handle->port]; +bool key_event[RARCH_CUSTOM_BIND_LIST_END]; #ifdef HAVE_MENU bool menu_is_alive = menu_driver_is_alive(); #endif @@ -100,23 +102,33 @@ void input_mapper_poll(input_mapper_t *handle) #endif memset(handle->keys, 0, sizeof(handle->keys)); - - for (i = 0; i < RARCH_CUSTOM_BIND_LIST_END; i++) + i = 0; + for (i = 0; i < 8; i++) { - if (i < RETROK_LAST) + for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) { - if (input_state(handle->port, RETRO_DEVICE_JOYPAD, 0, i)) + if (j < RETROK_LAST) { - MAPPER_SET_KEY (handle, - settings->uints.input_keymapper_ids[i]); - input_keyboard_event(true, - settings->uints.input_keymapper_ids[i], - 0, 0, RETRO_DEVICE_KEYBOARD); + if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && settings->uints.input_keymapper_multi_ids[i][j] != RETROK_UNKNOWN) + { + MAPPER_SET_KEY (handle, + settings->uints.input_keymapper_multi_ids[i][j]); + input_keyboard_event(true, + settings->uints.input_keymapper_multi_ids[i][j], + 0, 0, RETRO_DEVICE_KEYBOARD); + key_event[j] = true; + } + else + { + if (key_event[j] == false && + settings->uints.input_keymapper_multi_ids[i][j] != RETROK_UNKNOWN) + { + input_keyboard_event(false, + settings->uints.input_keymapper_multi_ids[i][j], + 0, 0, RETRO_DEVICE_KEYBOARD); + } + } } - else - input_keyboard_event(false, - settings->uints.input_keymapper_ids[i], - 0, 0, RETRO_DEVICE_KEYBOARD); } } } diff --git a/input/input_remapping.c b/input/input_remapping.c index 5830565e09..a72af21937 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -98,6 +98,10 @@ bool input_remapping_load_file(void *data, const char *path) else settings->uints.input_keymapper_ids[j] = RETROK_UNKNOWN; } + if (config_get_int(conf, keymapper_ident[j], &key_remap)) + settings->uints.input_keymapper_multi_ids[i][j] = key_remap; + else + settings->uints.input_keymapper_multi_ids[i][j] = RETROK_UNKNOWN; } @@ -205,6 +209,7 @@ bool input_remapping_save_file(const char *path) if (settings->uints.keymapper_port == i && settings->uints.input_keymapper_ids[j] != RETROK_UNKNOWN) config_set_int(conf, keymapper_ident[j], settings->uints.input_keymapper_ids[j]); + } else { diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index a3679e66c0..fdad42fb50 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -631,15 +631,22 @@ static void menu_action_setting_disp_set_label_input_desc_kbd( char *s2, size_t len2) { char desc[PATH_MAX_LENGTH]; - unsigned key_id; + unsigned key_id, id; unsigned remap_id; + unsigned offset = 0; + settings_t *settings = config_get_ptr(); if (!settings) return; + offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - (MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + + id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; remap_id = - settings->uints.input_keymapper_ids[type - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN]; + settings->uints.input_keymapper_multi_ids[offset][id]; + + RARCH_LOG("o: %d, type: %d, remap_id: %d\n", offset, type, remap_id); for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) { diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index c77acb2af6..d83ae6b7cf 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3280,6 +3280,7 @@ static int menu_displaylist_parse_options_remappings( if (device == RETRO_DEVICE_KEYBOARD) { + for (int i = 0; i < 8; i++) for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) { unsigned user = settings->uints.keymapper_port + 1; @@ -3288,9 +3289,9 @@ static int menu_displaylist_parse_options_remappings( const struct retro_keybind *auto_bind = NULL; const struct retro_keybind *keybind = NULL; - keybind = &input_config_binds[settings->uints.keymapper_port][retro_id]; + keybind = &input_config_binds[i][retro_id]; auto_bind = (const struct retro_keybind*) - input_config_get_bind_auto(settings->uints.keymapper_port, retro_id); + input_config_get_bind_auto(i, retro_id); input_config_get_bind_string(descriptor, keybind, auto_bind, sizeof(descriptor)); @@ -3298,14 +3299,14 @@ static int menu_displaylist_parse_options_remappings( if(!strstr(descriptor, "Auto")) { const struct retro_keybind *keyptr = - &input_config_binds[settings->uints.keymapper_port][retro_id]; + &input_config_binds[i][retro_id]; strlcpy(descriptor, msg_hash_to_str(keyptr->enum_idx), sizeof(descriptor)); } menu_entries_append_enum(info->list, descriptor, "", MSG_UNKNOWN, - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + retro_id, 0, 0); + (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + retro_id) * (i + 1), 0, 0); } } } diff --git a/menu/menu_driver.h b/menu/menu_driver.h index b61131a33d..a18bf606b3 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -208,7 +208,7 @@ enum menu_settings_type MENU_SETTINGS_INPUT_DESC_BEGIN, MENU_SETTINGS_INPUT_DESC_END = MENU_SETTINGS_INPUT_DESC_BEGIN + (MAX_USERS * (RARCH_FIRST_CUSTOM_BIND + 4)), MENU_SETTINGS_INPUT_DESC_KBD_BEGIN, - MENU_SETTINGS_INPUT_DESC_KBD_END = MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + 135, + MENU_SETTINGS_INPUT_DESC_KBD_END = (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + 135) * MAX_USERS, MENU_SETTINGS_SUBSYSTEM_LOAD, From 2bfb5ec0dfe55fa2c66bbff8b6bba71189ecbb0d Mon Sep 17 00:00:00 2001 From: radius Date: Fri, 30 Mar 2018 11:56:01 -0500 Subject: [PATCH 144/517] remap-redux part 2: fix nits, hookup left/right callbacks --- Makefile.vita | 2 +- configuration.h | 1 - input/input_mapper.c | 2 +- input/input_remapping.c | 18 +----------------- menu/cbs/menu_cbs_left.c | 11 +++++++---- menu/cbs/menu_cbs_right.c | 11 +++++++---- 6 files changed, 17 insertions(+), 28 deletions(-) diff --git a/Makefile.vita b/Makefile.vita index 5e8f135011..efa4646cfb 100644 --- a/Makefile.vita +++ b/Makefile.vita @@ -33,7 +33,7 @@ else HAVE_ZLIB := 1 HAVE_7ZIP := 1 HAVE_VITA2D := 1 - HAVE_KEYMAPPER := 1 + HAVE_KEYMAPPER := 1 HAVE_NETWORKING := 1 HAVE_SOCKET_LEGACY := 1 HAVE_MENU := 1 diff --git a/configuration.h b/configuration.h index 30eaf38b9c..916c4fa648 100644 --- a/configuration.h +++ b/configuration.h @@ -379,7 +379,6 @@ typedef struct settings unsigned input_libretro_device[MAX_USERS]; unsigned input_analog_dpad_mode[MAX_USERS]; - unsigned input_keymapper_ids[RARCH_CUSTOM_BIND_LIST_END]; unsigned input_keymapper_multi_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END]; unsigned input_remap_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END]; diff --git a/input/input_mapper.c b/input/input_mapper.c index 1bc5c5c6f2..5d09aa580b 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -86,7 +86,7 @@ void input_mapper_poll(input_mapper_t *handle) int i, j; settings_t *settings = config_get_ptr(); unsigned device = settings->uints.input_libretro_device[handle->port]; -bool key_event[RARCH_CUSTOM_BIND_LIST_END]; + bool key_event[RARCH_CUSTOM_BIND_LIST_END]; #ifdef HAVE_MENU bool menu_is_alive = menu_driver_is_alive(); #endif diff --git a/input/input_remapping.c b/input/input_remapping.c index a72af21937..b1920ee0c0 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -86,18 +86,6 @@ bool input_remapping_load_file(void *data, const char *path) key_remap = -1; - if (settings->uints.keymapper_port == i) - { - if (config_get_int(conf, keymapper_ident[j], &key_remap)) - { - settings->uints.input_keymapper_ids[j] = key_remap; -#if 0 - RARCH_LOG ("%s: %u\n", keymapper_ident[j], settings->uints.input_keymapper_ids[j]); -#endif - } - else - settings->uints.input_keymapper_ids[j] = RETROK_UNKNOWN; - } if (config_get_int(conf, keymapper_ident[j], &key_remap)) settings->uints.input_keymapper_multi_ids[i][j] = key_remap; else @@ -206,10 +194,6 @@ bool input_remapping_save_file(const char *path) else config_unset(conf,key_ident[j]); - if (settings->uints.keymapper_port == i && - settings->uints.input_keymapper_ids[j] != RETROK_UNKNOWN) - config_set_int(conf, keymapper_ident[j], settings->uints.input_keymapper_ids[j]); - } else { @@ -278,7 +262,7 @@ void input_remapping_set_defaults(bool deinit) const struct retro_keybind *keybind = &input_config_binds[i][j]; if (keybind) settings->uints.input_remap_ids[i][j] = keybind->id; - settings->uints.input_keymapper_ids[j] = RETROK_UNKNOWN; + settings->uints.input_keymapper_multi_ids[i][j] = RETROK_UNKNOWN; } for (j = 0; j < 4; j++) diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 230b6fea0d..4b579fe169 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -112,15 +112,18 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, bool wraparound) { char desc[PATH_MAX_LENGTH]; - unsigned key_id; + unsigned key_id, id, offset; unsigned remap_id; - unsigned offset = type - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; settings_t *settings = config_get_ptr(); if (!settings) return 0; - remap_id = settings->uints.input_keymapper_ids[offset]; + offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - (MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; + + remap_id = + settings->uints.input_keymapper_multi_ids[offset][id]; for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) { @@ -133,7 +136,7 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, else key_id = MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; - settings->uints.input_keymapper_ids[offset] = key_descriptors[key_id].key; + settings->uints.input_keymapper_multi_ids[offset][id] = key_descriptors[key_id].key; return 0; } diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 9469cbc98b..f98f1e72a3 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -105,16 +105,19 @@ int action_right_cheat(unsigned type, const char *label, int action_right_input_desc_kbd(unsigned type, const char *label, bool wraparound) { - unsigned key_id; + unsigned key_id, id, offset; unsigned remap_id; char desc[PATH_MAX_LENGTH]; - unsigned offset = type - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; settings_t *settings = config_get_ptr(); if (!settings) return 0; - remap_id = settings->uints.input_keymapper_ids[offset]; + offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - (MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; + + remap_id = + settings->uints.input_keymapper_multi_ids[offset][id]; for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) { @@ -127,7 +130,7 @@ int action_right_input_desc_kbd(unsigned type, const char *label, else key_id = 0; - settings->uints.input_keymapper_ids[offset] = key_descriptors[key_id].key; + settings->uints.input_keymapper_multi_ids[offset][id] = key_descriptors[key_id].key; return 0; } From c57f8722e5b7015407994a571a24e9ea51fd246b Mon Sep 17 00:00:00 2001 From: radius Date: Fri, 30 Mar 2018 13:08:58 -0500 Subject: [PATCH 145/517] remap-redux part 2: restore original var names --- configuration.h | 2 +- input/input_mapper.c | 10 +++++----- input/input_remapping.c | 6 +++--- menu/cbs/menu_cbs_get_value.c | 2 +- menu/cbs/menu_cbs_left.c | 4 ++-- menu/cbs/menu_cbs_right.c | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/configuration.h b/configuration.h index 916c4fa648..f3b8e90b14 100644 --- a/configuration.h +++ b/configuration.h @@ -379,7 +379,7 @@ typedef struct settings unsigned input_libretro_device[MAX_USERS]; unsigned input_analog_dpad_mode[MAX_USERS]; - unsigned input_keymapper_multi_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END]; + unsigned input_keymapper_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END]; unsigned input_remap_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END]; diff --git a/input/input_mapper.c b/input/input_mapper.c index 5d09aa580b..68bfe29e0b 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -109,22 +109,22 @@ void input_mapper_poll(input_mapper_t *handle) { if (j < RETROK_LAST) { - if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && settings->uints.input_keymapper_multi_ids[i][j] != RETROK_UNKNOWN) + if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) { MAPPER_SET_KEY (handle, - settings->uints.input_keymapper_multi_ids[i][j]); + settings->uints.input_keymapper_ids[i][j]); input_keyboard_event(true, - settings->uints.input_keymapper_multi_ids[i][j], + settings->uints.input_keymapper_ids[i][j], 0, 0, RETRO_DEVICE_KEYBOARD); key_event[j] = true; } else { if (key_event[j] == false && - settings->uints.input_keymapper_multi_ids[i][j] != RETROK_UNKNOWN) + settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) { input_keyboard_event(false, - settings->uints.input_keymapper_multi_ids[i][j], + settings->uints.input_keymapper_ids[i][j], 0, 0, RETRO_DEVICE_KEYBOARD); } } diff --git a/input/input_remapping.c b/input/input_remapping.c index b1920ee0c0..ad41480c26 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -87,9 +87,9 @@ bool input_remapping_load_file(void *data, const char *path) key_remap = -1; if (config_get_int(conf, keymapper_ident[j], &key_remap)) - settings->uints.input_keymapper_multi_ids[i][j] = key_remap; + settings->uints.input_keymapper_ids[i][j] = key_remap; else - settings->uints.input_keymapper_multi_ids[i][j] = RETROK_UNKNOWN; + settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; } @@ -262,7 +262,7 @@ void input_remapping_set_defaults(bool deinit) const struct retro_keybind *keybind = &input_config_binds[i][j]; if (keybind) settings->uints.input_remap_ids[i][j] = keybind->id; - settings->uints.input_keymapper_multi_ids[i][j] = RETROK_UNKNOWN; + settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; } for (j = 0; j < 4; j++) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index fdad42fb50..9743ba6c4b 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -644,7 +644,7 @@ static void menu_action_setting_disp_set_label_input_desc_kbd( id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; remap_id = - settings->uints.input_keymapper_multi_ids[offset][id]; + settings->uints.input_keymapper_ids[offset][id]; RARCH_LOG("o: %d, type: %d, remap_id: %d\n", offset, type, remap_id); diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 4b579fe169..ab5aaad644 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -123,7 +123,7 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; remap_id = - settings->uints.input_keymapper_multi_ids[offset][id]; + settings->uints.input_keymapper_ids[offset][id]; for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) { @@ -136,7 +136,7 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, else key_id = MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; - settings->uints.input_keymapper_multi_ids[offset][id] = key_descriptors[key_id].key; + settings->uints.input_keymapper_ids[offset][id] = key_descriptors[key_id].key; return 0; } diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index f98f1e72a3..a5d9de6188 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -117,7 +117,7 @@ int action_right_input_desc_kbd(unsigned type, const char *label, id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; remap_id = - settings->uints.input_keymapper_multi_ids[offset][id]; + settings->uints.input_keymapper_ids[offset][id]; for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) { @@ -130,7 +130,7 @@ int action_right_input_desc_kbd(unsigned type, const char *label, else key_id = 0; - settings->uints.input_keymapper_multi_ids[offset][id] = key_descriptors[key_id].key; + settings->uints.input_keymapper_ids[offset][id] = key_descriptors[key_id].key; return 0; } From daf262da9649ab9e5cb41415962c3ea9f7a5fc71 Mon Sep 17 00:00:00 2001 From: radius Date: Fri, 30 Mar 2018 23:06:32 -0500 Subject: [PATCH 146/517] remap-redux part 2: reimplement saving --- .vscode/c_cpp_properties.json | 250 +++++++++++++++++----------------- 1 file changed, 126 insertions(+), 124 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 81c2529184..52c61506df 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,125 +1,127 @@ -{ - "configurations": [ - { - "name": "Mac", - "includePath": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - }, - "macFrameworkPath": [ - "/System/Library/Frameworks", - "/Library/Frameworks" - ] - }, - { - "name": "Linux", - "includePath": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "Win32", - "includePath": [ - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "msys2-mingw32", - "includePath": [ - "C:/msys64/mingw32/include", - "C:/msys64/mingw32/i686-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/msys64/mingw32/include", - "C:/msys64/mingw32/i686-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "msys2-mingw64", - "includePath": [ - "C:/msys64/mingw64/include", - "C:/msys64/mingw64/x86_64-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/msys64/mingw64/include", - "C:/msys64/mingw64/x86_64-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - } - ], - "version": 3 +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "macFrameworkPath": [ + "/System/Library/Frameworks", + "/Library/Frameworks" + ] + }, + { + "name": "Linux", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "Win32", + "includePath": [ + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", + "${workspaceRoot}" + ], + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "cStandard": "c11", + "cppStandard": "c++17" + }, + { + "name": "msys2-mingw32", + "includePath": [ + "C:/msys64/mingw32/include", + "C:/msys64/mingw32/i686-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/msys64/mingw32/include", + "C:/msys64/mingw32/i686-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "msys2-mingw64", + "includePath": [ + "C:/msys64/mingw64/include", + "C:/msys64/mingw64/x86_64-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/msys64/mingw64/include", + "C:/msys64/mingw64/x86_64-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + } + ], + "version": 3 } \ No newline at end of file From f6ee035011bd6b86e67e29938c34b5043838b7a0 Mon Sep 17 00:00:00 2001 From: radius Date: Fri, 30 Mar 2018 23:28:20 -0500 Subject: [PATCH 147/517] remap-redux part 2: only add items for devices set to RETRO_DEVICE_KEYBOARD (or a subclass) --- input/input_remapping.c | 1 - menu/menu_displaylist.c | 57 ++++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/input/input_remapping.c b/input/input_remapping.c index ad41480c26..d79970f088 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -263,7 +263,6 @@ void input_remapping_set_defaults(bool deinit) if (keybind) settings->uints.input_remap_ids[i][j] = keybind->id; settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; - } for (j = 0; j < 4; j++) settings->uints.input_remap_ids[i][RARCH_FIRST_CUSTOM_BIND + j] = j; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index d83ae6b7cf..8f026068fd 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3274,39 +3274,42 @@ static int menu_displaylist_parse_options_remappings( if (system) { settings_t *settings = config_get_ptr(); + unsigned device; - unsigned device = settings->uints.input_libretro_device[settings->uints.keymapper_port]; - device &= RETRO_DEVICE_MASK; - - if (device == RETRO_DEVICE_KEYBOARD) + for (int i = 0; i < 8; i++) { - for (int i = 0; i < 8; i++) - for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) + device = settings->uints.input_libretro_device[i]; + device &= RETRO_DEVICE_MASK; + + if (device == RETRO_DEVICE_KEYBOARD) { - unsigned user = settings->uints.keymapper_port + 1; - unsigned desc_offset = retro_id; - char descriptor[255]; - const struct retro_keybind *auto_bind = NULL; - const struct retro_keybind *keybind = NULL; - - keybind = &input_config_binds[i][retro_id]; - auto_bind = (const struct retro_keybind*) - input_config_get_bind_auto(i, retro_id); - - input_config_get_bind_string(descriptor, - keybind, auto_bind, sizeof(descriptor)); - - if(!strstr(descriptor, "Auto")) + for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) { - const struct retro_keybind *keyptr = - &input_config_binds[i][retro_id]; + unsigned user = settings->uints.keymapper_port + 1; + unsigned desc_offset = retro_id; + char descriptor[255]; + const struct retro_keybind *auto_bind = NULL; + const struct retro_keybind *keybind = NULL; - strlcpy(descriptor, msg_hash_to_str(keyptr->enum_idx), sizeof(descriptor)); + keybind = &input_config_binds[i][retro_id]; + auto_bind = (const struct retro_keybind*) + input_config_get_bind_auto(i, retro_id); + + input_config_get_bind_string(descriptor, + keybind, auto_bind, sizeof(descriptor)); + + if(!strstr(descriptor, "Auto")) + { + const struct retro_keybind *keyptr = + &input_config_binds[i][retro_id]; + + strlcpy(descriptor, msg_hash_to_str(keyptr->enum_idx), sizeof(descriptor)); + } + + menu_entries_append_enum(info->list, descriptor, "", + MSG_UNKNOWN, + (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + retro_id) * (i + 1), 0, 0); } - - menu_entries_append_enum(info->list, descriptor, "", - MSG_UNKNOWN, - (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + retro_id) * (i + 1), 0, 0); } } } From db5f3d7a7511a1a73e88f0b16493053ab5478a7d Mon Sep 17 00:00:00 2001 From: radius Date: Fri, 30 Mar 2018 23:47:49 -0500 Subject: [PATCH 148/517] remap-redux part 2: add keymapper sublabels --- menu/cbs/menu_cbs_sublabel.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 25b3ae9aed..f6dfda3a9e 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -39,6 +39,7 @@ #include "../../retroarch.h" #include "../../content.h" +#include "../../configuration.h" #define default_sublabel_macro(func_name, lbl) \ static int (func_name)(file_list_t *list, unsigned type, unsigned i, const char *label, const char *path, char *s, size_t len) \ @@ -425,7 +426,27 @@ static int action_bind_sublabel_subsystem_add( return 0; } +static int action_bind_sublabel_remap_sublabel( + file_list_t *list, + unsigned type, unsigned i, + const char *label, const char *path, + char *s, size_t len) +{ + char desc[PATH_MAX_LENGTH]; + unsigned offset; + settings_t *settings = config_get_ptr(); + if (!settings) + return 0; + + offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - (MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + snprintf(s, len, "User #%d: %s", offset + 1, + input_config_get_device_display_name(offset) ? + input_config_get_device_display_name(offset) : + (input_config_get_device_name(offset) ? + input_config_get_device_name(offset) : "N/A")); + return 0; +} #ifdef HAVE_NETWORKING static int action_bind_sublabel_netplay_room( @@ -485,6 +506,14 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_generic); +#ifdef HAVE_KEYMAPPER + if (type >= MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + && type <= MENU_SETTINGS_INPUT_DESC_KBD_END) + { + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_remap_sublabel); + } +#endif + if (cbs->enum_idx != MSG_UNKNOWN) { switch (cbs->enum_idx) From 51edf47ed21230bdaf1e65ef1a2e0541c3f92887 Mon Sep 17 00:00:00 2001 From: radius Date: Sat, 31 Mar 2018 09:31:52 -0500 Subject: [PATCH 149/517] remap-redux part 2: cleanup keymapper code --- .vscode/settings.json | 1 + input/input_mapper.c | 5 +++-- input/input_remapping.c | 7 ++++++- menu/cbs/menu_cbs_get_value.c | 9 +++++---- menu/cbs/menu_cbs_left.c | 8 ++++++-- menu/cbs/menu_cbs_right.c | 8 ++++++-- menu/cbs/menu_cbs_sublabel.c | 5 ++++- menu/menu_displaylist.c | 2 +- 8 files changed, 32 insertions(+), 13 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e90ca77e4a..88513c30c2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,4 +14,5 @@ "*.in": "c", "*.rh": "c" }, + "C_Cpp.dimInactiveRegions": false, } \ No newline at end of file diff --git a/input/input_mapper.c b/input/input_mapper.c index 68bfe29e0b..42e4762040 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -103,13 +103,14 @@ void input_mapper_poll(input_mapper_t *handle) memset(handle->keys, 0, sizeof(handle->keys)); i = 0; - for (i = 0; i < 8; i++) + for (i = 0; i < MAX_USERS; i++) { for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) { if (j < RETROK_LAST) { - if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) + if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && + settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) { MAPPER_SET_KEY (handle, settings->uints.input_keymapper_ids[i][j]); diff --git a/input/input_remapping.c b/input/input_remapping.c index d79970f088..e820c162cd 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -194,11 +194,16 @@ bool input_remapping_save_file(const char *path) else config_unset(conf,key_ident[j]); + if (settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) + config_set_int(conf, keymapper_ident[j], + settings->uints.input_keymapper_ids[i][j]); + } else { if(settings->uints.input_remap_ids[i][j] != j - RARCH_FIRST_CUSTOM_BIND) - config_set_int(conf, key_ident[j], settings->uints.input_remap_ids[i][j]); + config_set_int(conf, key_ident[j], + settings->uints.input_remap_ids[i][j]); else config_unset(conf,key_ident[j]); } diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 9743ba6c4b..bd764c518b 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -640,15 +640,16 @@ static void menu_action_setting_disp_set_label_input_desc_kbd( if (!settings) return; - offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - (MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - + (MENU_SETTINGS_INPUT_DESC_KBD_END - + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; remap_id = settings->uints.input_keymapper_ids[offset][id]; - RARCH_LOG("o: %d, type: %d, remap_id: %d\n", offset, type, remap_id); - - for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) + for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) { if(remap_id == key_descriptors[key_id].key) break; diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index ab5aaad644..83b7a5446b 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -119,13 +119,17 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, if (!settings) return 0; - offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - (MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - + (MENU_SETTINGS_INPUT_DESC_KBD_END - + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; remap_id = settings->uints.input_keymapper_ids[offset][id]; - for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) + for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) { if(remap_id == key_descriptors[key_id].key) break; diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index a5d9de6188..132426f9db 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -113,13 +113,17 @@ int action_right_input_desc_kbd(unsigned type, const char *label, if (!settings) return 0; - offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - (MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - + (MENU_SETTINGS_INPUT_DESC_KBD_END - + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; remap_id = settings->uints.input_keymapper_ids[offset][id]; - for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) + for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) { if(remap_id == key_descriptors[key_id].key) break; diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index f6dfda3a9e..81403b20a6 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -439,7 +439,10 @@ static int action_bind_sublabel_remap_sublabel( if (!settings) return 0; - offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - (MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - + (MENU_SETTINGS_INPUT_DESC_KBD_END - + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; + snprintf(s, len, "User #%d: %s", offset + 1, input_config_get_device_display_name(offset) ? input_config_get_device_display_name(offset) : diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 8f026068fd..207ea58981 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3276,7 +3276,7 @@ static int menu_displaylist_parse_options_remappings( settings_t *settings = config_get_ptr(); unsigned device; - for (int i = 0; i < 8; i++) + for (int i = 0; i < MAX_USERS; i++) { device = settings->uints.input_libretro_device[i]; device &= RETRO_DEVICE_MASK; From 34649d1abf25c422e439a5f864a20e3f11fdb063 Mon Sep 17 00:00:00 2001 From: radius Date: Sat, 31 Mar 2018 11:13:00 -0500 Subject: [PATCH 150/517] remap-redux part 2: start inverting the gamepad mapper columns --- input/input_defines.h | 2 + menu/cbs/menu_cbs_get_value.c | 78 ++++++++++++++++------------------- menu/menu_cbs.c | 2 +- menu/menu_displaylist.c | 61 ++++++++++++++++----------- menu/menu_driver.h | 2 +- 5 files changed, 76 insertions(+), 69 deletions(-) diff --git a/input/input_defines.h b/input/input_defines.h index 91f14d2766..4f203d4f46 100644 --- a/input/input_defines.h +++ b/input/input_defines.h @@ -28,6 +28,8 @@ RETRO_BEGIN_DECLS #define MAX_INPUT_DEVICES 16 +#define MAX_KEYS 136 + #define RARCH_FIRST_CUSTOM_BIND 16 #define RARCH_FIRST_LIGHTGUN_BIND RARCH_ANALOG_BIND_LIST_END #define RARCH_FIRST_MISC_CUSTOM_BIND RARCH_LIGHTGUN_BIND_LIST_END diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index bd764c518b..36f35b5092 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -547,72 +547,60 @@ static void menu_action_setting_disp_set_label_input_desc( const char *path, char *s2, size_t len2) { - char descriptor[255]; + rarch_system_info_t *system = NULL; + const char* descriptor = NULL; const struct retro_keybind *auto_bind = NULL; const struct retro_keybind *keybind = NULL; settings_t *settings = config_get_ptr(); unsigned inp_desc_index_offset = type - MENU_SETTINGS_INPUT_DESC_BEGIN; unsigned inp_desc_user = inp_desc_index_offset / - (RARCH_FIRST_CUSTOM_BIND + 4); + (RARCH_FIRST_CUSTOM_BIND + 8); unsigned inp_desc_button_index_offset = inp_desc_index_offset - - (inp_desc_user * (RARCH_FIRST_CUSTOM_BIND + 4)); + (inp_desc_user * (RARCH_FIRST_CUSTOM_BIND + 8)); unsigned remap_id = 0; - if (!settings) + system = runloop_get_system_info(); + + if (!system) return; - descriptor[0] = '\0'; - - remap_id = settings->uints.input_remap_ids - [inp_desc_user][inp_desc_button_index_offset]; - - keybind = &input_config_binds[inp_desc_user][remap_id]; - auto_bind = (const struct retro_keybind*) - input_config_get_bind_auto(inp_desc_user, remap_id); - - input_config_get_bind_string(descriptor, - keybind, auto_bind, sizeof(descriptor)); - - if (inp_desc_button_index_offset < RARCH_FIRST_CUSTOM_BIND) - { - if(strstr(descriptor, "Auto") && !strstr(descriptor, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE))) - strlcpy(s, - descriptor, - len); - else - { - const struct retro_keybind *keyptr = &input_config_binds[inp_desc_user] - [remap_id]; - - strlcpy(s, msg_hash_to_str(keyptr->enum_idx), len); - } - } - - + descriptor = system->input_desc_btn[inp_desc_user][inp_desc_button_index_offset]; + if (inp_desc_button_index_offset < RARCH_FIRST_CUSTOM_BIND + 8) + strlcpy(s, descriptor ? descriptor : "---", len); else { - const char *str = NULL; switch (remap_id) { case 0: - str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X); + descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_PLUS); break; case 1: - str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y); + descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_MINUS); break; case 2: - str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X); + descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_PLUS); break; case 3: - str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y); + descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_MINUS); + break; + case 4: + descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_PLUS); + break; + case 5: + descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_MINUS); + break; + case 6: + descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_PLUS); + break; + case 7: + descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_MINUS); break; } - if (!string_is_empty(str)) - strlcpy(s, str, len); + if (!string_is_empty(descriptor)) + strlcpy(s, descriptor, len); } *w = 19; @@ -654,8 +642,14 @@ static void menu_action_setting_disp_set_label_input_desc_kbd( if(remap_id == key_descriptors[key_id].key) break; } - snprintf(desc, sizeof(desc), "Keyboard %s", key_descriptors[key_id].desc); - strlcpy(s, desc, len); + + if (key_descriptors[key_id].key != RETROK_FIRST) + { + snprintf(desc, sizeof(desc), "Keyboard %s", key_descriptors[key_id].desc); + strlcpy(s, desc, len); + } + else + strlcpy(s, "---", len); *w = 19; strlcpy(s2, path, len2); diff --git a/menu/menu_cbs.c b/menu/menu_cbs.c index 28dd055114..230839574e 100644 --- a/menu/menu_cbs.c +++ b/menu/menu_cbs.c @@ -32,7 +32,7 @@ static void menu_cbs_init_log(const char *entry_label, const char *bind_label, c #endif } -struct key_desc key_descriptors[MENU_SETTINGS_INPUT_DESC_KBD_END] = +struct key_desc key_descriptors[136] = { {RETROK_FIRST, "Unmapped"}, {RETROK_BACKSPACE, "Backspace"}, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 207ea58981..b3ba5fb744 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3240,33 +3240,44 @@ static int menu_displaylist_parse_options_remappings( if (system) { + settings_t *settings = config_get_ptr(); + unsigned device; for (p = 0; p < max_users; p++) { - for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND + 4; retro_id++) + device = settings->uints.input_libretro_device[p]; + device &= RETRO_DEVICE_MASK; + + if (device == RETRO_DEVICE_JOYPAD || device == RETRO_DEVICE_ANALOG) { - char desc_label[64]; - unsigned user = p + 1; - unsigned desc_offset = retro_id; - const char *description = NULL; + for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND + 8; retro_id++) + { + char desc_label[64]; + unsigned user = p + 1; + unsigned desc_offset = retro_id; + char descriptor[255]; + const struct retro_keybind *auto_bind = NULL; + const struct retro_keybind *keybind = NULL; - desc_label[0] = '\0'; + keybind = &input_config_binds[p][retro_id]; + auto_bind = (const struct retro_keybind*) + input_config_get_bind_auto(p, retro_id); - if (desc_offset >= RARCH_FIRST_CUSTOM_BIND) - desc_offset = RARCH_FIRST_CUSTOM_BIND - + (desc_offset - RARCH_FIRST_CUSTOM_BIND) * 2; + input_config_get_bind_string(descriptor, + keybind, auto_bind, sizeof(descriptor)); - description = system->input_desc_btn[p][desc_offset]; + if(!strstr(descriptor, "Auto")) + { + const struct retro_keybind *keyptr = + &input_config_binds[p][retro_id]; - if (!description) - continue; + strlcpy(descriptor, msg_hash_to_str(keyptr->enum_idx), sizeof(descriptor)); + } - snprintf(desc_label, sizeof(desc_label), - "%s %u %s : ", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_USER), - user, description); - menu_entries_append_enum(info->list, desc_label, "", - MSG_UNKNOWN, - MENU_SETTINGS_INPUT_DESC_BEGIN + - (p * (RARCH_FIRST_CUSTOM_BIND + 4)) + retro_id, 0, 0); + menu_entries_append_enum(info->list, descriptor, "", + MSG_UNKNOWN, + MENU_SETTINGS_INPUT_DESC_BEGIN + + (p * (RARCH_FIRST_CUSTOM_BIND + 8)) + retro_id, 0, 0); + } } } } @@ -3276,9 +3287,9 @@ static int menu_displaylist_parse_options_remappings( settings_t *settings = config_get_ptr(); unsigned device; - for (int i = 0; i < MAX_USERS; i++) + for (p = 0; p < MAX_USERS; p++) { - device = settings->uints.input_libretro_device[i]; + device = settings->uints.input_libretro_device[p]; device &= RETRO_DEVICE_MASK; if (device == RETRO_DEVICE_KEYBOARD) @@ -3291,9 +3302,9 @@ static int menu_displaylist_parse_options_remappings( const struct retro_keybind *auto_bind = NULL; const struct retro_keybind *keybind = NULL; - keybind = &input_config_binds[i][retro_id]; + keybind = &input_config_binds[p][retro_id]; auto_bind = (const struct retro_keybind*) - input_config_get_bind_auto(i, retro_id); + input_config_get_bind_auto(p, retro_id); input_config_get_bind_string(descriptor, keybind, auto_bind, sizeof(descriptor)); @@ -3301,14 +3312,14 @@ static int menu_displaylist_parse_options_remappings( if(!strstr(descriptor, "Auto")) { const struct retro_keybind *keyptr = - &input_config_binds[i][retro_id]; + &input_config_binds[p][retro_id]; strlcpy(descriptor, msg_hash_to_str(keyptr->enum_idx), sizeof(descriptor)); } menu_entries_append_enum(info->list, descriptor, "", MSG_UNKNOWN, - (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + retro_id) * (i + 1), 0, 0); + (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + retro_id) * (p + 1), 0, 0); } } } diff --git a/menu/menu_driver.h b/menu/menu_driver.h index a18bf606b3..c4df110eb8 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -208,7 +208,7 @@ enum menu_settings_type MENU_SETTINGS_INPUT_DESC_BEGIN, MENU_SETTINGS_INPUT_DESC_END = MENU_SETTINGS_INPUT_DESC_BEGIN + (MAX_USERS * (RARCH_FIRST_CUSTOM_BIND + 4)), MENU_SETTINGS_INPUT_DESC_KBD_BEGIN, - MENU_SETTINGS_INPUT_DESC_KBD_END = (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + 135) * MAX_USERS, + MENU_SETTINGS_INPUT_DESC_KBD_END = (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + 136) * MAX_USERS, MENU_SETTINGS_SUBSYSTEM_LOAD, From c4754815b613b14ad883c544e73f80b987cc58b0 Mon Sep 17 00:00:00 2001 From: radius Date: Sat, 31 Mar 2018 11:35:29 -0500 Subject: [PATCH 151/517] remap-redux part 2: fix small issue with keymapper --- input/input_defines.h | 2 +- menu/cbs/menu_cbs_left.c | 4 ++-- menu/cbs/menu_cbs_right.c | 2 +- menu/menu_cbs.c | 2 +- menu/menu_driver.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/input/input_defines.h b/input/input_defines.h index 4f203d4f46..a7be2367d3 100644 --- a/input/input_defines.h +++ b/input/input_defines.h @@ -28,7 +28,7 @@ RETRO_BEGIN_DECLS #define MAX_INPUT_DEVICES 16 -#define MAX_KEYS 136 +#define RARCH_MAX_KEYS 136 #define RARCH_FIRST_CUSTOM_BIND 16 #define RARCH_FIRST_LIGHTGUN_BIND RARCH_ANALOG_BIND_LIST_END diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 83b7a5446b..b2980932a5 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -128,7 +128,7 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, remap_id = settings->uints.input_keymapper_ids[offset][id]; - for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - + for (key_id = 0; key_id < RARCH_MAX_KEYS + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) { if(remap_id == key_descriptors[key_id].key) @@ -138,7 +138,7 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, if (key_id > 0) key_id--; else - key_id = MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; + key_id = RARCH_MAX_KEYS + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; settings->uints.input_keymapper_ids[offset][id] = key_descriptors[key_id].key; diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 132426f9db..c76f7e1dd3 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -129,7 +129,7 @@ int action_right_input_desc_kbd(unsigned type, const char *label, break; } - if (key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN) + if (key_id < RARCH_MAX_KEYS + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN) key_id++; else key_id = 0; diff --git a/menu/menu_cbs.c b/menu/menu_cbs.c index 230839574e..33d4b5a01e 100644 --- a/menu/menu_cbs.c +++ b/menu/menu_cbs.c @@ -32,7 +32,7 @@ static void menu_cbs_init_log(const char *entry_label, const char *bind_label, c #endif } -struct key_desc key_descriptors[136] = +struct key_desc key_descriptors[RARCH_MAX_KEYS] = { {RETROK_FIRST, "Unmapped"}, {RETROK_BACKSPACE, "Backspace"}, diff --git a/menu/menu_driver.h b/menu/menu_driver.h index c4df110eb8..ebc589b306 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -208,7 +208,7 @@ enum menu_settings_type MENU_SETTINGS_INPUT_DESC_BEGIN, MENU_SETTINGS_INPUT_DESC_END = MENU_SETTINGS_INPUT_DESC_BEGIN + (MAX_USERS * (RARCH_FIRST_CUSTOM_BIND + 4)), MENU_SETTINGS_INPUT_DESC_KBD_BEGIN, - MENU_SETTINGS_INPUT_DESC_KBD_END = (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + 136) * MAX_USERS, + MENU_SETTINGS_INPUT_DESC_KBD_END = (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + RARCH_MAX_KEYS) * MAX_USERS, MENU_SETTINGS_SUBSYSTEM_LOAD, From 052887d8b019a32af8f3f76068232a0a07b4a091 Mon Sep 17 00:00:00 2001 From: radius Date: Sat, 31 Mar 2018 11:45:36 -0500 Subject: [PATCH 152/517] remap-redux part 2: fix redefinition of key_descriptors --- menu/cbs/menu_cbs_get_value.c | 2 +- menu/cbs/menu_cbs_left.c | 2 +- menu/cbs/menu_cbs_right.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 36f35b5092..cbfac887e2 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -54,7 +54,7 @@ cbs->action_get_value_ident = #name; #endif -extern struct key_desc key_descriptors[MENU_SETTINGS_INPUT_DESC_KBD_END]; +extern struct key_desc key_descriptors[RARCH_MAX_KEYS]; static void menu_action_setting_disp_set_label_cheat_num_passes( file_list_t* list, diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index b2980932a5..7619614734 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -46,7 +46,7 @@ } while(0) #endif -extern struct key_desc key_descriptors[MENU_SETTINGS_INPUT_DESC_KBD_END]; +extern struct key_desc key_descriptors[RARCH_MAX_KEYS]; static int generic_shader_action_parameter_left( struct video_shader_parameter *param, diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index c76f7e1dd3..bf6838fb50 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -48,7 +48,7 @@ } while(0) #endif -extern struct key_desc key_descriptors[MENU_SETTINGS_INPUT_DESC_KBD_END]; +extern struct key_desc key_descriptors[RARCH_MAX_KEYS]; static int generic_shader_action_parameter_right(struct video_shader_parameter *param, unsigned type, const char *label, bool wraparound) From 7f5fe5ebff876f5450db9dae2d730b9f7151b06c Mon Sep 17 00:00:00 2001 From: radius Date: Sun, 1 Apr 2018 20:57:33 -0500 Subject: [PATCH 153/517] remap-redux part 2: add controller sublabel --- input/input_driver.c | 26 ++++++----- input/input_mapper.c | 85 +++++++++++++++++++++++------------ input/input_mapper.h | 5 ++- menu/cbs/menu_cbs_get_value.c | 59 +++++++----------------- menu/cbs/menu_cbs_left.c | 25 ++++++----- menu/cbs/menu_cbs_right.c | 35 ++++++++------- menu/cbs/menu_cbs_sublabel.c | 34 +++++++++++++- menu/menu_driver.h | 4 +- 8 files changed, 158 insertions(+), 115 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 14ed1c17a9..e3032c405c 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -634,7 +634,8 @@ void input_poll(void) int16_t input_state(unsigned port, unsigned device, unsigned idx, unsigned id) { - int16_t res = 0; + int16_t res = 0; + bool clear = false; device &= RETRO_DEVICE_MASK; @@ -657,8 +658,11 @@ int16_t input_state(unsigned port, unsigned device, switch (device) { case RETRO_DEVICE_JOYPAD: - if (id < RARCH_FIRST_CUSTOM_BIND) - id = settings->uints.input_remap_ids[port][id]; + if (id != settings->uints.input_remap_ids[port][id]) + { + clear = true; + } + break; case RETRO_DEVICE_ANALOG: if (idx < 2 && id < 2) @@ -676,11 +680,9 @@ int16_t input_state(unsigned port, unsigned device, if (((id < RARCH_FIRST_META_KEY) || (device == RETRO_DEVICE_KEYBOARD))) { bool bind_valid = libretro_input_binds[port] && libretro_input_binds[port][id].valid; - + rarch_joypad_info_t joypad_info; if (bind_valid || device == RETRO_DEVICE_KEYBOARD) { - rarch_joypad_info_t joypad_info; - joypad_info.axis_threshold = input_driver_axis_threshold; joypad_info.joy_idx = settings->uints.input_joypad_map[port]; joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx]; @@ -690,6 +692,12 @@ int16_t input_state(unsigned port, unsigned device, } } +#ifdef HAVE_KEYMAPPER + if (input_driver_mapper) + input_mapper_state(input_driver_mapper, + &res, port, device, idx, id, clear); +#endif + #ifdef HAVE_OVERLAY if (overlay_ptr) input_state_overlay(overlay_ptr, &res, port, device, idx, id); @@ -700,12 +708,6 @@ int16_t input_state(unsigned port, unsigned device, input_remote_state(&res, port, device, idx, id); #endif -#ifdef HAVE_KEYMAPPER - if (input_driver_mapper) - input_mapper_state(input_driver_mapper, - &res, port, device, idx, id); -#endif - /* Don't allow turbo for D-pad. */ if (device == RETRO_DEVICE_JOYPAD && (id < RETRO_DEVICE_ID_JOYPAD_UP || id > RETRO_DEVICE_ID_JOYPAD_RIGHT)) diff --git a/input/input_mapper.c b/input/input_mapper.c index 42e4762040..b21d09ac22 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -81,6 +81,13 @@ void input_mapper_free(input_mapper_t *handle) free (handle); } +bool flag = false; + +bool input_mapper_button_pressed(input_mapper_t *handle, int id) +{ + return (handle->buttons >> id) & 1U; +} + void input_mapper_poll(input_mapper_t *handle) { int i, j; @@ -93,9 +100,6 @@ void input_mapper_poll(input_mapper_t *handle) device &= RETRO_DEVICE_MASK; - /* for now we only handle keyboard inputs */ - if (device != RETRO_DEVICE_KEYBOARD) - return; #ifdef HAVE_MENU if (menu_is_alive) return; @@ -103,31 +107,56 @@ void input_mapper_poll(input_mapper_t *handle) memset(handle->keys, 0, sizeof(handle->keys)); i = 0; - for (i = 0; i < MAX_USERS; i++) + if (device == RETRO_DEVICE_KEYBOARD) { - for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) + for (i = 0; i < MAX_USERS; i++) { - if (j < RETROK_LAST) + for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) { - if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && - settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) + if (j < RETROK_LAST) { - MAPPER_SET_KEY (handle, - settings->uints.input_keymapper_ids[i][j]); - input_keyboard_event(true, - settings->uints.input_keymapper_ids[i][j], - 0, 0, RETRO_DEVICE_KEYBOARD); - key_event[j] = true; - } - else - { - if (key_event[j] == false && + if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) { - input_keyboard_event(false, + MAPPER_SET_KEY (handle, + settings->uints.input_keymapper_ids[i][j]); + input_keyboard_event(true, settings->uints.input_keymapper_ids[i][j], 0, 0, RETRO_DEVICE_KEYBOARD); + key_event[j] = true; } + else + { + if (key_event[j] == false && + settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) + { + input_keyboard_event(false, + settings->uints.input_keymapper_ids[i][j], + 0, 0, RETRO_DEVICE_KEYBOARD); + } + } + } + } + } + } + if (device == RETRO_DEVICE_JOYPAD) + { + for (i = 0; i < MAX_USERS; i++) + { + for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) + { + if(input_state(i, RETRO_DEVICE_JOYPAD, i, j)) + { + if (j != settings->uints.input_remap_ids[i][j]) + { + RARCH_LOG("remapped button pressed: old:%d new: %d\n", j, settings->uints.input_remap_ids[i][j]); + handle->buttons |= 1 << settings->uints.input_remap_ids[i][j]; + } + } + if(!input_state(i, RETRO_DEVICE_JOYPAD, i, j)) + { + if (j != settings->uints.input_remap_ids[i][j]) + handle->buttons &= ~(1 << settings->uints.input_remap_ids[i][j]); } } } @@ -140,24 +169,23 @@ void input_mapper_state( unsigned port, unsigned device, unsigned idx, - unsigned id) + unsigned id, + bool clear) { if (!handle) return; switch (device) { + case RETRO_DEVICE_JOYPAD: + /* we should get the new buttons here via input_remapper_button_pressed but it doesn't work because the old state is still there + so both actions trigger */ + if (input_mapper_button_pressed(handle, id)) + *ret = 1; + break; case RETRO_DEVICE_KEYBOARD: if (id < RETROK_LAST) { - /* - RARCH_LOG("State: UDLR %u %u %u %u\n", - MAPPER_GET_KEY(handle, RETROK_UP), - MAPPER_GET_KEY(handle, RETROK_DOWN), - MAPPER_GET_KEY(handle, RETROK_LEFT), - MAPPER_GET_KEY(handle, RETROK_RIGHT) - );*/ - if (MAPPER_GET_KEY(handle, id)) *ret |= 1; } @@ -165,4 +193,5 @@ void input_mapper_state( default: break; } + return; } diff --git a/input/input_mapper.h b/input/input_mapper.h index c50519acd8..fd40cba04d 100644 --- a/input/input_mapper.h +++ b/input/input_mapper.h @@ -37,7 +37,7 @@ void input_mapper_free(input_mapper_t *handle); void input_mapper_poll(input_mapper_t *handle); -bool input_mapper_key_pressed(int key); +bool input_mapper_key_pressed(input_mapper_t *handle, int key); void input_mapper_state( input_mapper_t *handle, @@ -45,7 +45,8 @@ void input_mapper_state( unsigned port, unsigned device, unsigned idx, - unsigned id); + unsigned id, + bool clear); RETRO_END_DECLS diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index cbfac887e2..ed1b625075 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -552,56 +552,29 @@ static void menu_action_setting_disp_set_label_input_desc( const struct retro_keybind *auto_bind = NULL; const struct retro_keybind *keybind = NULL; settings_t *settings = config_get_ptr(); - unsigned inp_desc_index_offset = - type - MENU_SETTINGS_INPUT_DESC_BEGIN; - unsigned inp_desc_user = inp_desc_index_offset / - (RARCH_FIRST_CUSTOM_BIND + 8); - unsigned inp_desc_button_index_offset = inp_desc_index_offset - - (inp_desc_user * (RARCH_FIRST_CUSTOM_BIND + 8)); - unsigned remap_id = 0; + + unsigned key_id, id, offset; + unsigned remap_id = 0; + + if (!settings) + return 0; + + offset = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); + + id = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * offset; + + remap_id = + settings->uints.input_remap_ids[offset][id]; system = runloop_get_system_info(); if (!system) return; - descriptor = system->input_desc_btn[inp_desc_user][inp_desc_button_index_offset]; + descriptor = system->input_desc_btn[offset][remap_id]; - if (inp_desc_button_index_offset < RARCH_FIRST_CUSTOM_BIND + 8) - strlcpy(s, descriptor ? descriptor : "---", len); - else - { - switch (remap_id) - { - case 0: - descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_PLUS); - break; - case 1: - descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_MINUS); - break; - case 2: - descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_PLUS); - break; - case 3: - descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_MINUS); - break; - case 4: - descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_PLUS); - break; - case 5: - descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_MINUS); - break; - case 6: - descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_PLUS); - break; - case 7: - descriptor = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_MINUS); - break; - } - - if (!string_is_empty(descriptor)) - strlcpy(s, descriptor, len); - } + if (!string_is_empty(descriptor)) + strlcpy(s, descriptor, len); *w = 19; strlcpy(s2, path, len2); diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 7619614734..f27952c779 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -90,19 +90,24 @@ static int action_left_cheat(unsigned type, const char *label, wraparound); } +/* fix-me: incomplete, lacks error checking */ static int action_left_input_desc(unsigned type, const char *label, - bool wraparound) + bool wraparound) { - unsigned inp_desc_index_offset = type - - MENU_SETTINGS_INPUT_DESC_BEGIN; - unsigned inp_desc_user = inp_desc_index_offset / - (RARCH_FIRST_CUSTOM_BIND + 4); - unsigned inp_desc_button_index_offset = inp_desc_index_offset - - (inp_desc_user * (RARCH_FIRST_CUSTOM_BIND + 4)); - settings_t *settings = config_get_ptr(); - if (settings->uints.input_remap_ids[inp_desc_user][inp_desc_button_index_offset] > 0) - settings->uints.input_remap_ids[inp_desc_user][inp_desc_button_index_offset]--; + unsigned key_id, id, offset; + unsigned remap_id = 0; + settings_t *settings = config_get_ptr(); + + if (!settings) + return 0; + + offset = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); + + id = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * offset; + + if (settings->uints.input_remap_ids[offset][id] > 0) + settings->uints.input_remap_ids[offset][id]--; return 0; } diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index bf6838fb50..9dd8e1fd40 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -107,7 +107,6 @@ int action_right_input_desc_kbd(unsigned type, const char *label, { unsigned key_id, id, offset; unsigned remap_id; - char desc[PATH_MAX_LENGTH]; settings_t *settings = config_get_ptr(); if (!settings) @@ -129,6 +128,8 @@ int action_right_input_desc_kbd(unsigned type, const char *label, break; } + RARCH_LOG("o:%u t:%u i:%u r:%u\n", offset, type, id, remap_id); + if (key_id < RARCH_MAX_KEYS + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN) key_id++; else @@ -140,26 +141,28 @@ int action_right_input_desc_kbd(unsigned type, const char *label, } #endif +/* fix-me: incomplete, lacks error checking */ int action_right_input_desc(unsigned type, const char *label, bool wraparound) { -unsigned inp_desc_index_offset = type - MENU_SETTINGS_INPUT_DESC_BEGIN; -unsigned inp_desc_user = inp_desc_index_offset / (RARCH_FIRST_CUSTOM_BIND + 4); -unsigned inp_desc_button_index_offset = inp_desc_index_offset - (inp_desc_user * (RARCH_FIRST_CUSTOM_BIND + 4)); -settings_t *settings = config_get_ptr(); -if (inp_desc_button_index_offset < RARCH_FIRST_CUSTOM_BIND) -{ - if (settings->uints.input_remap_ids[inp_desc_user][inp_desc_button_index_offset] < RARCH_FIRST_CUSTOM_BIND - 1) - settings->uints.input_remap_ids[inp_desc_user][inp_desc_button_index_offset]++; -} -else -{ - if (settings->uints.input_remap_ids[inp_desc_user][inp_desc_button_index_offset] < 4 - 1) - settings->uints.input_remap_ids[inp_desc_user][inp_desc_button_index_offset]++; -} + unsigned key_id, id, offset; + unsigned remap_id = 0; + settings_t *settings = config_get_ptr(); -return 0; + if (!settings) + return 0; + + offset = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); + + id = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * offset; + + if (settings->uints.input_remap_ids[offset][id] < RARCH_FIRST_CUSTOM_BIND) + settings->uints.input_remap_ids[offset][id]++; + + RARCH_LOG("o:%u t:%u i:%u r:%u\n", offset, type, id, remap_id); + + return 0; } static int action_right_scroll(unsigned type, const char *label, diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 81403b20a6..3facf11794 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -426,7 +426,7 @@ static int action_bind_sublabel_subsystem_add( return 0; } -static int action_bind_sublabel_remap_sublabel( +static int action_bind_sublabel_remap_kbd_sublabel( file_list_t *list, unsigned type, unsigned i, const char *label, const char *path, @@ -451,6 +451,30 @@ static int action_bind_sublabel_remap_sublabel( return 0; } + +static int action_bind_sublabel_remap_sublabel( + file_list_t *list, + unsigned type, unsigned i, + const char *label, const char *path, + char *s, size_t len) +{ + char desc[PATH_MAX_LENGTH]; + unsigned offset; + settings_t *settings = config_get_ptr(); + + if (!settings) + return 0; + + offset = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); + + snprintf(s, len, "User #%d: %s", offset + 1, + input_config_get_device_display_name(offset) ? + input_config_get_device_display_name(offset) : + (input_config_get_device_name(offset) ? + input_config_get_device_name(offset) : "N/A")); + return 0; +} + #ifdef HAVE_NETWORKING static int action_bind_sublabel_netplay_room( file_list_t *list, @@ -513,10 +537,16 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, if (type >= MENU_SETTINGS_INPUT_DESC_KBD_BEGIN && type <= MENU_SETTINGS_INPUT_DESC_KBD_END) { - BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_remap_sublabel); + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_remap_kbd_sublabel); } #endif + if (type >= MENU_SETTINGS_INPUT_DESC_BEGIN + && type <= MENU_SETTINGS_INPUT_DESC_END) + { + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_remap_sublabel); + } + if (cbs->enum_idx != MSG_UNKNOWN) { switch (cbs->enum_idx) diff --git a/menu/menu_driver.h b/menu/menu_driver.h index ebc589b306..94195c73d9 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -206,9 +206,9 @@ enum menu_settings_type MENU_SETTINGS_CHEAT_BEGIN, MENU_SETTINGS_CHEAT_END = MENU_SETTINGS_CHEAT_BEGIN + (MAX_CHEAT_COUNTERS - 1), MENU_SETTINGS_INPUT_DESC_BEGIN, - MENU_SETTINGS_INPUT_DESC_END = MENU_SETTINGS_INPUT_DESC_BEGIN + (MAX_USERS * (RARCH_FIRST_CUSTOM_BIND + 4)), + MENU_SETTINGS_INPUT_DESC_END = MENU_SETTINGS_INPUT_DESC_BEGIN + ((RARCH_FIRST_CUSTOM_BIND + 8) * MAX_USERS), MENU_SETTINGS_INPUT_DESC_KBD_BEGIN, - MENU_SETTINGS_INPUT_DESC_KBD_END = (MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + RARCH_MAX_KEYS) * MAX_USERS, + MENU_SETTINGS_INPUT_DESC_KBD_END = MENU_SETTINGS_INPUT_DESC_KBD_BEGIN + (RARCH_MAX_KEYS * MAX_USERS), MENU_SETTINGS_SUBSYSTEM_LOAD, From be2c648596b0332b80c2c1c5fa15f0a4209c4c98 Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 2 Apr 2018 00:08:40 -0500 Subject: [PATCH 154/517] remap-redux part2: after 60 attempsts, new mapper works, N:1 mapping too --- input/input_driver.c | 21 ++++++++++++--------- input/input_mapper.c | 24 +++++++++--------------- menu/cbs/menu_cbs_get_value.c | 2 +- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index e3032c405c..d307f102a7 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -579,6 +579,8 @@ void input_poll(void) if (input_driver_block_libretro_input) return; + + for (i = 0; i < max_users; i++) { if (libretro_input_binds[i][RARCH_TURBO_ENABLE].valid) @@ -603,6 +605,11 @@ void input_poll(void) input_driver_axis_threshold); #endif +#ifdef HAVE_KEYMAPPER + if (input_driver_mapper) + input_mapper_poll(input_driver_mapper); +#endif + #ifdef HAVE_COMMAND if (input_driver_command) command_poll(input_driver_command); @@ -612,11 +619,6 @@ void input_poll(void) if (input_driver_remote) input_remote_poll(input_driver_remote, max_users); #endif - -#ifdef HAVE_KEYMAPPER - if (input_driver_mapper) - input_mapper_poll(input_driver_mapper); -#endif } /** @@ -659,9 +661,7 @@ int16_t input_state(unsigned port, unsigned device, { case RETRO_DEVICE_JOYPAD: if (id != settings->uints.input_remap_ids[port][id]) - { clear = true; - } break; case RETRO_DEVICE_ANALOG: @@ -687,8 +687,11 @@ int16_t input_state(unsigned port, unsigned device, joypad_info.joy_idx = settings->uints.input_joypad_map[port]; joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx]; - res = current_input->input_state( - current_input_data, joypad_info, libretro_input_binds, port, device, idx, id); + if (!clear) + res = current_input->input_state( + current_input_data, joypad_info, libretro_input_binds, port, device, idx, id); + else + res = 0; } } diff --git a/input/input_mapper.c b/input/input_mapper.c index b21d09ac22..6dcd2de98e 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -58,7 +58,7 @@ struct input_mapper /* the whole keyboard state */ uint32_t keys[RETROK_LAST / 32 + 1]; /* This is a bitmask of (1 << key_bind_id). */ - uint64_t buttons; + retro_bits_t buttons; }; input_mapper_t *input_mapper_new(uint16_t port) @@ -85,7 +85,7 @@ bool flag = false; bool input_mapper_button_pressed(input_mapper_t *handle, int id) { - return (handle->buttons >> id) & 1U; + return BIT256_GET(handle->buttons, id); } void input_mapper_poll(input_mapper_t *handle) @@ -141,23 +141,17 @@ void input_mapper_poll(input_mapper_t *handle) } if (device == RETRO_DEVICE_JOYPAD) { + retro_bits_t current_input; + input_keys_pressed(settings, ¤t_input); + BIT256_CLEAR_ALL(handle->buttons); for (i = 0; i < MAX_USERS; i++) { for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) { - if(input_state(i, RETRO_DEVICE_JOYPAD, i, j)) - { - if (j != settings->uints.input_remap_ids[i][j]) - { - RARCH_LOG("remapped button pressed: old:%d new: %d\n", j, settings->uints.input_remap_ids[i][j]); - handle->buttons |= 1 << settings->uints.input_remap_ids[i][j]; - } - } - if(!input_state(i, RETRO_DEVICE_JOYPAD, i, j)) - { - if (j != settings->uints.input_remap_ids[i][j]) - handle->buttons &= ~(1 << settings->uints.input_remap_ids[i][j]); - } + int aux = BIT256_GET(current_input, j); + int remap = settings->uints.input_remap_ids[i][j]; + if (aux == 1 && j != remap) + BIT256_SET(handle->buttons, remap); } } } diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index ed1b625075..e19a4db51f 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -557,7 +557,7 @@ static void menu_action_setting_disp_set_label_input_desc( unsigned remap_id = 0; if (!settings) - return 0; + return; offset = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); From f7b94e5f66ae1cb9cfe3c8ae88504efcb4596a08 Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 2 Apr 2018 20:27:51 -0500 Subject: [PATCH 155/517] remap-redux part2: clean up gamepad mapper callbacks --- menu/cbs/menu_cbs_get_value.c | 11 +++++------ menu/cbs/menu_cbs_left.c | 14 +++++--------- menu/cbs/menu_cbs_right.c | 14 +++++--------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index e19a4db51f..489da78c4d 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -553,25 +553,24 @@ static void menu_action_setting_disp_set_label_input_desc( const struct retro_keybind *keybind = NULL; settings_t *settings = config_get_ptr(); - unsigned key_id, id, offset; + unsigned btn_idx, user_idx; unsigned remap_id = 0; if (!settings) return; - offset = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); - - id = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * offset; + user_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); + btn_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * user_idx; remap_id = - settings->uints.input_remap_ids[offset][id]; + settings->uints.input_remap_ids[user_idx][btn_idx]; system = runloop_get_system_info(); if (!system) return; - descriptor = system->input_desc_btn[offset][remap_id]; + descriptor = system->input_desc_btn[user_idx][remap_id]; if (!string_is_empty(descriptor)) strlcpy(s, descriptor, len); diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index f27952c779..a4015d1c6d 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -90,24 +90,20 @@ static int action_left_cheat(unsigned type, const char *label, wraparound); } -/* fix-me: incomplete, lacks error checking */ static int action_left_input_desc(unsigned type, const char *label, bool wraparound) { - - unsigned key_id, id, offset; - unsigned remap_id = 0; + unsigned btn_idx, user_idx; settings_t *settings = config_get_ptr(); if (!settings) return 0; - offset = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); + user_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); + btn_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * user_idx; - id = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * offset; - - if (settings->uints.input_remap_ids[offset][id] > 0) - settings->uints.input_remap_ids[offset][id]--; + if (settings->uints.input_remap_ids[user_idx][btn_idx] > 0) + settings->uints.input_remap_ids[user_idx][btn_idx]--; return 0; } diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 9dd8e1fd40..44141a1b05 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -146,21 +146,17 @@ int action_right_input_desc(unsigned type, const char *label, bool wraparound) { - unsigned key_id, id, offset; - unsigned remap_id = 0; + unsigned btn_idx, user_idx; settings_t *settings = config_get_ptr(); if (!settings) return 0; - offset = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); + user_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); + btn_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * user_idx; - id = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * offset; - - if (settings->uints.input_remap_ids[offset][id] < RARCH_FIRST_CUSTOM_BIND) - settings->uints.input_remap_ids[offset][id]++; - - RARCH_LOG("o:%u t:%u i:%u r:%u\n", offset, type, id, remap_id); + if (settings->uints.input_remap_ids[user_idx][btn_idx] < RARCH_FIRST_CUSTOM_BIND) + settings->uints.input_remap_ids[user_idx][btn_idx]++; return 0; } From e71826e3d64e9207503d634b7df910072f268e43 Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 2 Apr 2018 20:29:47 -0500 Subject: [PATCH 156/517] remap-redux part2: clean up gamepad mapper callbacks --- menu/cbs/menu_cbs_get_value.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 489da78c4d..5c3f92c50c 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -547,14 +547,13 @@ static void menu_action_setting_disp_set_label_input_desc( const char *path, char *s2, size_t len2) { - rarch_system_info_t *system = NULL; - const char* descriptor = NULL; + rarch_system_info_t *system = NULL; + settings_t *settings = config_get_ptr(); + const char* descriptor = NULL; const struct retro_keybind *auto_bind = NULL; const struct retro_keybind *keybind = NULL; - settings_t *settings = config_get_ptr(); - unsigned btn_idx, user_idx; - unsigned remap_id = 0; + unsigned btn_idx, user_idx, remap_idx; if (!settings) return; @@ -562,7 +561,7 @@ static void menu_action_setting_disp_set_label_input_desc( user_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); btn_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * user_idx; - remap_id = + remap_idx = settings->uints.input_remap_ids[user_idx][btn_idx]; system = runloop_get_system_info(); @@ -570,7 +569,7 @@ static void menu_action_setting_disp_set_label_input_desc( if (!system) return; - descriptor = system->input_desc_btn[user_idx][remap_id]; + descriptor = system->input_desc_btn[user_idx][remap_idx]; if (!string_is_empty(descriptor)) strlcpy(s, descriptor, len); From 921a30da88f1977d68b1afa158a6b2e6bab66311 Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 2 Apr 2018 21:12:05 -0500 Subject: [PATCH 157/517] remap-redux part2: add empty description --- menu/cbs/menu_cbs_get_value.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 5c3f92c50c..07382ac111 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -573,6 +573,9 @@ static void menu_action_setting_disp_set_label_input_desc( if (!string_is_empty(descriptor)) strlcpy(s, descriptor, len); + else + strlcpy(s, "---", len); + *w = 19; strlcpy(s2, path, len2); From 058b4cea7958d0fba9038f003e0f0beb58e72008 Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 2 Apr 2018 21:22:09 -0500 Subject: [PATCH 158/517] remap-redux part2: add empty description to analogs that are currently not implemented --- menu/cbs/menu_cbs_get_value.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 07382ac111..838e0d413a 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -569,7 +569,8 @@ static void menu_action_setting_disp_set_label_input_desc( if (!system) return; - descriptor = system->input_desc_btn[user_idx][remap_idx]; + if (btn_idx < RARCH_FIRST_CUSTOM_BIND) + descriptor = system->input_desc_btn[user_idx][remap_idx]; if (!string_is_empty(descriptor)) strlcpy(s, descriptor, len); From 59da4b880e65921205ae00892268e8ec9931c44d Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 2 Apr 2018 21:47:10 -0500 Subject: [PATCH 159/517] remap-redux part2: add empty description to analogs that are currently not remappable --- menu/cbs/menu_cbs_get_value.c | 1 - menu/cbs/menu_cbs_right.c | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 838e0d413a..387594ddf2 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -580,7 +580,6 @@ static void menu_action_setting_disp_set_label_input_desc( *w = 19; strlcpy(s2, path, len2); - } #ifdef HAVE_KEYMAPPER diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 44141a1b05..88c42f0549 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -155,6 +155,11 @@ int action_right_input_desc(unsigned type, const char *label, user_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); btn_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * user_idx; +#if 1 + for (int i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) + RARCH_LOG("[remap-debug]: user %d button %d new id %d\n", user_idx, i, settings->uints.input_remap_ids[user_idx][i]); +#endif + if (settings->uints.input_remap_ids[user_idx][btn_idx] < RARCH_FIRST_CUSTOM_BIND) settings->uints.input_remap_ids[user_idx][btn_idx]++; From 23331aa48412a41e3f2f54e4644b738251da7c6f Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 2 Apr 2018 22:05:48 -0500 Subject: [PATCH 160/517] remap-redux part2: wraparound --- menu/cbs/menu_cbs_get_value.c | 6 +----- menu/cbs/menu_cbs_left.c | 14 +++++++++++--- menu/cbs/menu_cbs_right.c | 27 ++++++++++++++++++--------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 387594ddf2..5233673a5c 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -547,11 +547,9 @@ static void menu_action_setting_disp_set_label_input_desc( const char *path, char *s2, size_t len2) { - rarch_system_info_t *system = NULL; + rarch_system_info_t *system = runloop_get_system_info(); settings_t *settings = config_get_ptr(); const char* descriptor = NULL; - const struct retro_keybind *auto_bind = NULL; - const struct retro_keybind *keybind = NULL; unsigned btn_idx, user_idx, remap_idx; @@ -564,8 +562,6 @@ static void menu_action_setting_disp_set_label_input_desc( remap_idx = settings->uints.input_remap_ids[user_idx][btn_idx]; - system = runloop_get_system_info(); - if (!system) return; diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index a4015d1c6d..4bc7b093a1 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -93,10 +93,11 @@ static int action_left_cheat(unsigned type, const char *label, static int action_left_input_desc(unsigned type, const char *label, bool wraparound) { - unsigned btn_idx, user_idx; - settings_t *settings = config_get_ptr(); + rarch_system_info_t *system = runloop_get_system_info(); + settings_t *settings = config_get_ptr(); + unsigned btn_idx, user_idx, remap_idx; - if (!settings) + if (!settings || !system) return 0; user_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); @@ -104,6 +105,13 @@ static int action_left_input_desc(unsigned type, const char *label, if (settings->uints.input_remap_ids[user_idx][btn_idx] > 0) settings->uints.input_remap_ids[user_idx][btn_idx]--; + else + settings->uints.input_remap_ids[user_idx][btn_idx] = RARCH_FIRST_CUSTOM_BIND; + + /* skip the not used button (unless they are at the end by calling the right desc function recursively */ + remap_idx = settings->uints.input_remap_ids[user_idx][btn_idx]; + if (string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_FIRST_CUSTOM_BIND) + action_left_input_desc(type, label, wraparound); return 0; } diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 88c42f0549..dd984f7856 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -145,23 +145,32 @@ int action_right_input_desc_kbd(unsigned type, const char *label, int action_right_input_desc(unsigned type, const char *label, bool wraparound) { + rarch_system_info_t *system = runloop_get_system_info(); + settings_t *settings = config_get_ptr(); + unsigned btn_idx, user_idx, remap_idx; - unsigned btn_idx, user_idx; - settings_t *settings = config_get_ptr(); - - if (!settings) + if (!settings || !system) return 0; user_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); btn_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * user_idx; -#if 1 - for (int i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) - RARCH_LOG("[remap-debug]: user %d button %d new id %d\n", user_idx, i, settings->uints.input_remap_ids[user_idx][i]); -#endif - if (settings->uints.input_remap_ids[user_idx][btn_idx] < RARCH_FIRST_CUSTOM_BIND) settings->uints.input_remap_ids[user_idx][btn_idx]++; + else + settings->uints.input_remap_ids[user_idx][btn_idx] = 0; + + /* skip the not used button (unless they are at the end by calling the right desc function recursively */ + remap_idx = settings->uints.input_remap_ids[user_idx][btn_idx]; + if (string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_FIRST_CUSTOM_BIND) + action_right_input_desc(type, label, wraparound); + +#if 0 + int i = 0; + RARCH_LOG("[remap-debug] new descriptor for %d: %s\n", remap_idx, system->input_desc_btn[user_idx][remap_idx]); + for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) + RARCH_LOG("[remap-debug]: user %d button %d new id %d\n", user_idx, i, settings->uints.input_remap_ids[user_idx][i]); +#endif return 0; } From 5efba1c257b5ab2be91b76d1e850d25472b522b4 Mon Sep 17 00:00:00 2001 From: radius Date: Tue, 3 Apr 2018 00:21:33 -0500 Subject: [PATCH 161/517] remap-redux part2: better wraparound, skip analogs, add RARCH_UNMAPPED --- input/input_defines.h | 2 ++ input/input_driver.c | 7 ---- input/input_mapper.c | 63 +++++++++++++++++++++++++++++++---- input/input_remapping.c | 23 ++++++++----- menu/cbs/menu_cbs_get_value.c | 3 ++ menu/cbs/menu_cbs_left.c | 14 ++++++-- menu/cbs/menu_cbs_right.c | 15 ++++++--- 7 files changed, 97 insertions(+), 30 deletions(-) diff --git a/input/input_defines.h b/input/input_defines.h index a7be2367d3..12e2be7e97 100644 --- a/input/input_defines.h +++ b/input/input_defines.h @@ -35,6 +35,8 @@ RETRO_BEGIN_DECLS #define RARCH_FIRST_MISC_CUSTOM_BIND RARCH_LIGHTGUN_BIND_LIST_END #define RARCH_FIRST_META_KEY RARCH_CUSTOM_BIND_LIST_END +#define RARCH_UNMAPPED 1024 + /* RetroArch specific bind IDs. */ enum { diff --git a/input/input_driver.c b/input/input_driver.c index d307f102a7..1e694ef4e0 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -666,13 +666,6 @@ int16_t input_state(unsigned port, unsigned device, break; case RETRO_DEVICE_ANALOG: if (idx < 2 && id < 2) - { - unsigned new_id = RARCH_FIRST_CUSTOM_BIND + (idx * 2 + id); - - new_id = settings->uints.input_remap_ids[port][new_id]; - idx = (new_id & 2) >> 1; - id = new_id & 1; - } break; } } diff --git a/input/input_mapper.c b/input/input_mapper.c index 6dcd2de98e..64c95cf02d 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -92,7 +92,10 @@ void input_mapper_poll(input_mapper_t *handle) { int i, j; settings_t *settings = config_get_ptr(); + retro_bits_t current_input; unsigned device = settings->uints.input_libretro_device[handle->port]; + unsigned current_button_value; + unsigned remap_button; bool key_event[RARCH_CUSTOM_BIND_LIST_END]; #ifdef HAVE_MENU bool menu_is_alive = menu_driver_is_alive(); @@ -141,18 +144,66 @@ void input_mapper_poll(input_mapper_t *handle) } if (device == RETRO_DEVICE_JOYPAD) { - retro_bits_t current_input; input_keys_pressed(settings, ¤t_input); BIT256_CLEAR_ALL(handle->buttons); for (i = 0; i < MAX_USERS; i++) { - for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) + /* this loop iterates on all users and all buttons, and checks if a pressed button + is assigned to any other button than the default one, then it sets the bit on the + mapper input bitmap, later on the original input is cleared in input_state */ + + for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) { - int aux = BIT256_GET(current_input, j); - int remap = settings->uints.input_remap_ids[i][j]; - if (aux == 1 && j != remap) - BIT256_SET(handle->buttons, remap); + current_button_value = BIT256_GET(current_input, j); + remap_button = settings->uints.input_remap_ids[i][j]; + if (current_button_value == 1 && j != remap_button && + remap_button != RARCH_UNMAPPED) + BIT256_SET(handle->buttons, remap_button); } +#if 0 + /* --CURRENTLY NOT IMPLEMENTED-- + this loop should iterate on all users and all analog stick axes and if the axes are + moved and is assigned to a button it should set the bit on the mapper input bitmap. + Once implemented we should make sure to clear the original analog + stick input in input_state in input_driver.c */ + + for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_CUSTOM_BIND_LIST_END; j++) + { + + } +#endif + } + } + if (device == RETRO_DEVICE_ANALOG) + { + input_keys_pressed(settings, ¤t_input); + BIT256_CLEAR_ALL(handle->buttons); + for (i = 0; i < MAX_USERS; i++) + { + /* this loop iterates on all users and all buttons, and checks if a pressed button + is assigned to any other button than the default one, then it sets the bit on the + mapper input bitmap, later on the original input is cleared in input_state */ + + for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) + { + current_button_value = BIT256_GET(current_input, j); + remap_button = settings->uints.input_remap_ids[i][j]; + if (current_button_value == 1 && j != remap_button && + remap_button != RARCH_UNMAPPED) + BIT256_SET(handle->buttons, remap_button); + } +#if 0 + /* --CURRENTLY NOT IMPLEMENTED-- + this loop should iterate on all users and all analog stick axes and if the axes are + moved and is assigned to a button or another stick, it should set the bit on the + mapper input bitmap. Once implemented we should make sure to clear the original analog + stick input in input_state in input_driver.c */ + + for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_CUSTOM_BIND_LIST_END; j++) + { + + } +#endif } } } diff --git a/input/input_remapping.c b/input/input_remapping.c index e820c162cd..43431a17fd 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -73,18 +73,21 @@ bool input_remapping_load_file(void *data, const char *path) for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 4; j++) { - int key_remap = -1; + int btn_remap = -1; fill_pathname_join_delim(key_ident[j], s1, key_strings[j], '_', sizeof(key_ident[j])); fill_pathname_join_delim(keymapper_ident[j], s2, key_strings[j], '_', sizeof(key_ident[j])); - if (config_get_int(conf, key_ident[j], &key_remap) - && key_remap < RARCH_FIRST_CUSTOM_BIND) - settings->uints.input_remap_ids[i][j] = key_remap; + if (config_get_int(conf, key_ident[j], &btn_remap) + && (btn_remap < RARCH_FIRST_CUSTOM_BIND && btn_remap != -1)) + settings->uints.input_remap_ids[i][j] = btn_remap; + else if (config_get_int(conf, key_ident[j], &btn_remap) + && (btn_remap == -1)) + settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; - key_remap = -1; + int key_remap = -1; if (config_get_int(conf, keymapper_ident[j], &key_remap)) settings->uints.input_keymapper_ids[i][j] = key_remap; @@ -96,7 +99,7 @@ bool input_remapping_load_file(void *data, const char *path) for (j = 0; j < 4; j++) { - int key_remap = -1; + int btn_remap = -1; snprintf(key_ident[RARCH_FIRST_CUSTOM_BIND + j], sizeof(key_ident[RARCH_FIRST_CUSTOM_BIND + j]), @@ -105,9 +108,9 @@ bool input_remapping_load_file(void *data, const char *path) key_strings[RARCH_FIRST_CUSTOM_BIND + j]); if (config_get_int(conf, key_ident[RARCH_FIRST_CUSTOM_BIND + j], - &key_remap) && (key_remap < 4)) + &btn_remap) && (btn_remap < 4)) settings->uints.input_remap_ids[i][RARCH_FIRST_CUSTOM_BIND + j] = - key_remap; + btn_remap; } snprintf(s1, sizeof(s1), "input_player%u_analog_dpad_mode", i + 1); @@ -189,8 +192,10 @@ bool input_remapping_save_file(const char *path) /* only save values that have been modified */ if(j < RARCH_FIRST_CUSTOM_BIND) { - if(settings->uints.input_remap_ids[i][j] != j) + if(settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] != RARCH_UNMAPPED) config_set_int(conf, key_ident[j], settings->uints.input_remap_ids[i][j]); + else if (settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] == RARCH_UNMAPPED) + config_set_int(conf, key_ident[j], -1); else config_unset(conf,key_ident[j]); diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 5233673a5c..f6bcfd117e 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -562,6 +562,9 @@ static void menu_action_setting_disp_set_label_input_desc( remap_idx = settings->uints.input_remap_ids[user_idx][btn_idx]; + if (remap_idx == RARCH_UNMAPPED) + settings->uints.input_remap_ids[user_idx][btn_idx] = RARCH_UNMAPPED; + if (!system) return; diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 4bc7b093a1..98e96b721b 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -103,14 +103,22 @@ static int action_left_input_desc(unsigned type, const char *label, user_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); btn_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * user_idx; + if (settings->uints.input_remap_ids[user_idx][btn_idx] == RARCH_UNMAPPED) + settings->uints.input_remap_ids[user_idx][btn_idx] = RARCH_CUSTOM_BIND_LIST_END - 1; + if (settings->uints.input_remap_ids[user_idx][btn_idx] > 0) settings->uints.input_remap_ids[user_idx][btn_idx]--; + else if (settings->uints.input_remap_ids[user_idx][btn_idx] == 0) + settings->uints.input_remap_ids[user_idx][btn_idx] = RARCH_UNMAPPED; else - settings->uints.input_remap_ids[user_idx][btn_idx] = RARCH_FIRST_CUSTOM_BIND; + settings->uints.input_remap_ids[user_idx][btn_idx] = RARCH_CUSTOM_BIND_LIST_END - 1; - /* skip the not used button (unless they are at the end by calling the right desc function recursively */ remap_idx = settings->uints.input_remap_ids[user_idx][btn_idx]; - if (string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_FIRST_CUSTOM_BIND) + + /* skip the not used buttons (unless they are at the end by calling the right desc function recursively + also skip all the axes until analog remapping is implemented */ + if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) || + (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)) action_left_input_desc(type, label, wraparound); return 0; diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index dd984f7856..ee6cbd85b2 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -155,20 +155,25 @@ int action_right_input_desc(unsigned type, const char *label, user_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) / (RARCH_FIRST_CUSTOM_BIND + 8); btn_idx = (type - MENU_SETTINGS_INPUT_DESC_BEGIN) - (RARCH_FIRST_CUSTOM_BIND + 8) * user_idx; - if (settings->uints.input_remap_ids[user_idx][btn_idx] < RARCH_FIRST_CUSTOM_BIND) + if (settings->uints.input_remap_ids[user_idx][btn_idx] < RARCH_CUSTOM_BIND_LIST_END - 1) settings->uints.input_remap_ids[user_idx][btn_idx]++; + else if (settings->uints.input_remap_ids[user_idx][btn_idx] == RARCH_CUSTOM_BIND_LIST_END - 1) + settings->uints.input_remap_ids[user_idx][btn_idx] = RARCH_UNMAPPED; else settings->uints.input_remap_ids[user_idx][btn_idx] = 0; - /* skip the not used button (unless they are at the end by calling the right desc function recursively */ remap_idx = settings->uints.input_remap_ids[user_idx][btn_idx]; - if (string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_FIRST_CUSTOM_BIND) + + /* skip the not used buttons (unless they are at the end by calling the right desc function recursively + also skip all the axes until analog remapping is implemented */ + if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) || + (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)) action_right_input_desc(type, label, wraparound); #if 0 int i = 0; - RARCH_LOG("[remap-debug] new descriptor for %d: %s\n", remap_idx, system->input_desc_btn[user_idx][remap_idx]); - for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) + //RARCH_LOG("[remap-debug] new descriptor for %d: %s\n", remap_idx, system->input_desc_btn[user_idx][remap_idx]); + for (i = 0; i < RARCH_ANALOG_BIND_LIST_END; i++) RARCH_LOG("[remap-debug]: user %d button %d new id %d\n", user_idx, i, settings->uints.input_remap_ids[user_idx][i]); #endif From e130afff739a9025b4b48be847020a23c18b429f Mon Sep 17 00:00:00 2001 From: radius Date: Tue, 3 Apr 2018 00:28:10 -0500 Subject: [PATCH 162/517] remap-redux part2: cleanup --- input/input_driver.c | 5 +++-- input/input_mapper.c | 6 +----- input/input_mapper.h | 3 +-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 1e694ef4e0..3432549352 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -673,9 +673,10 @@ int16_t input_state(unsigned port, unsigned device, if (((id < RARCH_FIRST_META_KEY) || (device == RETRO_DEVICE_KEYBOARD))) { bool bind_valid = libretro_input_binds[port] && libretro_input_binds[port][id].valid; - rarch_joypad_info_t joypad_info; + if (bind_valid || device == RETRO_DEVICE_KEYBOARD) { + rarch_joypad_info_t joypad_info; joypad_info.axis_threshold = input_driver_axis_threshold; joypad_info.joy_idx = settings->uints.input_joypad_map[port]; joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx]; @@ -691,7 +692,7 @@ int16_t input_state(unsigned port, unsigned device, #ifdef HAVE_KEYMAPPER if (input_driver_mapper) input_mapper_state(input_driver_mapper, - &res, port, device, idx, id, clear); + &res, port, device, idx, id); #endif #ifdef HAVE_OVERLAY diff --git a/input/input_mapper.c b/input/input_mapper.c index 64c95cf02d..e97ce1974f 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -47,7 +47,6 @@ #define MAPPER_GET_KEY(state, key) (((state)->keys[(key) / 32] >> ((key) % 32)) & 1) #define MAPPER_SET_KEY(state, key) (state)->keys[(key) / 32] |= 1 << ((key) % 32) -#define MAPPER_UNSET_KEY(state, key) (state)->keys[(key) / 32] |= 0 << ((key) % 32) struct input_mapper { @@ -214,8 +213,7 @@ void input_mapper_state( unsigned port, unsigned device, unsigned idx, - unsigned id, - bool clear) + unsigned id) { if (!handle) return; @@ -223,8 +221,6 @@ void input_mapper_state( switch (device) { case RETRO_DEVICE_JOYPAD: - /* we should get the new buttons here via input_remapper_button_pressed but it doesn't work because the old state is still there - so both actions trigger */ if (input_mapper_button_pressed(handle, id)) *ret = 1; break; diff --git a/input/input_mapper.h b/input/input_mapper.h index fd40cba04d..5f2b91e51b 100644 --- a/input/input_mapper.h +++ b/input/input_mapper.h @@ -45,8 +45,7 @@ void input_mapper_state( unsigned port, unsigned device, unsigned idx, - unsigned id, - bool clear); + unsigned id); RETRO_END_DECLS From 958216ede9d0b2f16c60f1e60e24c39d25854ba6 Mon Sep 17 00:00:00 2001 From: radius Date: Tue, 3 Apr 2018 00:30:58 -0500 Subject: [PATCH 163/517] remap-redux part2: change remap file labels because old ones are incompatible --- input/input_remapping.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/input/input_remapping.c b/input/input_remapping.c index 43431a17fd..370e31a6a9 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -68,7 +68,7 @@ bool input_remapping_load_file(void *data, const char *path) s1[0] = '\0'; s2[0] = '\0'; - snprintf(s1, sizeof(s1), "input_player%u", i + 1); + snprintf(s1, sizeof(s1), "input_player%u_btn", i + 1); snprintf(s2, sizeof(s2), "input_player%u_key", i + 1); for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 4; j++) @@ -179,7 +179,7 @@ bool input_remapping_save_file(const char *path) s1[0] = '\0'; s2[0] = '\0'; - snprintf(s1, sizeof(s1), "input_player%u", i + 1); + snprintf(s1, sizeof(s1), "input_player%u_btn", i + 1); snprintf(s2, sizeof(s2), "input_player%u_key", i + 1); for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 4; j++) From 2415f821afe5b190a5baba16523c8abafdbc8f4e Mon Sep 17 00:00:00 2001 From: radius Date: Tue, 3 Apr 2018 00:34:20 -0500 Subject: [PATCH 164/517] remap-redux part2: hide analogs for now --- menu/menu_displaylist.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index b3ba5fb744..106c16f445 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3249,7 +3249,8 @@ static int menu_displaylist_parse_options_remappings( if (device == RETRO_DEVICE_JOYPAD || device == RETRO_DEVICE_ANALOG) { - for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND + 8; retro_id++) + /* change to RARCH_FIRST_CUSTOM_BIND + 8 once analog remapping is implemented */ + for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) { char desc_label[64]; unsigned user = p + 1; From e42e79db28d451730b854d12032627a69d488109 Mon Sep 17 00:00:00 2001 From: radius Date: Tue, 3 Apr 2018 00:36:26 -0500 Subject: [PATCH 165/517] remap-redux part2: rename variable --- input/input_driver.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 3432549352..ba6b263f85 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -637,7 +637,10 @@ int16_t input_state(unsigned port, unsigned device, unsigned idx, unsigned id) { int16_t res = 0; - bool clear = false; + + /* used to reset input state of a button when the gamepad mapper + is in action for that button*/ + bool reset_state = false; device &= RETRO_DEVICE_MASK; @@ -661,7 +664,7 @@ int16_t input_state(unsigned port, unsigned device, { case RETRO_DEVICE_JOYPAD: if (id != settings->uints.input_remap_ids[port][id]) - clear = true; + reset_state = true; break; case RETRO_DEVICE_ANALOG: @@ -681,7 +684,7 @@ int16_t input_state(unsigned port, unsigned device, joypad_info.joy_idx = settings->uints.input_joypad_map[port]; joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx]; - if (!clear) + if (!reset_state) res = current_input->input_state( current_input_data, joypad_info, libretro_input_binds, port, device, idx, id); else From d8d22a9c4037e580b2383be9c6a4c542ae66c9dc Mon Sep 17 00:00:00 2001 From: radius Date: Tue, 3 Apr 2018 20:16:15 -0500 Subject: [PATCH 166/517] remap-redux part2: simplify this code a bit, still not working --- .vscode/launch.json | 43 ++++++------------------ input/input_driver.c | 29 ++++++++++++++++ input/input_driver.h | 2 ++ input/input_mapper.c | 78 ++++++++++++-------------------------------- 4 files changed, 62 insertions(+), 90 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 6e1f69631a..4880045ed9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,45 +4,22 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "name": "msys2-mingw64 debug", + { + "name": "(gdb) Attach", "type": "cppdbg", - "request": "launch", + "request": "attach", "program": "${workspaceFolder}/retroarch.exe", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": true, + "processId": "${command:pickProcess}", "MIMode": "gdb", "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } ] }, - { - "name": "msys2-mingw32 debug", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/retroarch.exe", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": true, - "MIMode": "gdb", - "miDebuggerPath": "c:\\msys64\\mingw32\\bin\\gdb.exe", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } + ] } \ No newline at end of file diff --git a/input/input_driver.c b/input/input_driver.c index ba6b263f85..14cd308b0d 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1127,6 +1127,35 @@ void input_keys_pressed(void *data, retro_bits_t* p_new_state) } } +void input_get_state_for_port(void *data, unsigned port, retro_bits_t* p_new_state) +{ + unsigned i; + rarch_joypad_info_t joypad_info; + settings_t *settings = (settings_t*)data; + const struct retro_keybind *binds = input_config_binds[port]; + + BIT256_CLEAR_ALL_PTR(p_new_state); + + joypad_info.joy_idx = settings->uints.input_joypad_map[port]; + joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx]; + joypad_info.axis_threshold = input_driver_axis_threshold; + + input_driver_block_libretro_input = false; + input_driver_block_hotkey = false; + + for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) + { + bool bit_pressed = false; + + if (binds[i].valid && current_input->input_state(current_input_data, + joypad_info, &binds, port, RETRO_DEVICE_JOYPAD, 0, i)) + bit_pressed = true; + + if (bit_pressed) + BIT256_SET_PTR(p_new_state, i); + } +} + void *input_driver_get_data(void) { return current_input_data; diff --git a/input/input_driver.h b/input/input_driver.h index 2ef47c7d24..07ff1a285f 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -343,6 +343,8 @@ void input_menu_keys_pressed(void *data, retro_bits_t* new_state); void *input_driver_get_data(void); +void input_get_state_for_port(void *data, unsigned port, retro_bits_t* p_new_state); + const input_driver_t *input_get_ptr(void); void *input_get_data(void); diff --git a/input/input_mapper.c b/input/input_mapper.c index e97ce1974f..e7853d5141 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -50,14 +50,12 @@ struct input_mapper { - /* The controller port that will be polled*/ - uint8_t port; /* Left X, Left Y, Right X, Right Y */ int16_t analog[4]; /* the whole keyboard state */ uint32_t keys[RETROK_LAST / 32 + 1]; /* This is a bitmask of (1 << key_bind_id). */ - retro_bits_t buttons; + retro_bits_t buttons[MAX_USERS]; }; input_mapper_t *input_mapper_new(uint16_t port) @@ -68,8 +66,6 @@ input_mapper_t *input_mapper_new(uint16_t port) if (!handle) return NULL; - handle->port = port; - return handle; } @@ -82,9 +78,9 @@ void input_mapper_free(input_mapper_t *handle) bool flag = false; -bool input_mapper_button_pressed(input_mapper_t *handle, int id) +bool input_mapper_button_pressed(input_mapper_t *handle, unsigned port, unsigned id) { - return BIT256_GET(handle->buttons, id); + return BIT256_GET(handle->buttons[port], id); } void input_mapper_poll(input_mapper_t *handle) @@ -92,7 +88,8 @@ void input_mapper_poll(input_mapper_t *handle) int i, j; settings_t *settings = config_get_ptr(); retro_bits_t current_input; - unsigned device = settings->uints.input_libretro_device[handle->port]; + unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); + unsigned device = 0; unsigned current_button_value; unsigned remap_button; bool key_event[RARCH_CUSTOM_BIND_LIST_END]; @@ -100,24 +97,25 @@ void input_mapper_poll(input_mapper_t *handle) bool menu_is_alive = menu_driver_is_alive(); #endif - device &= RETRO_DEVICE_MASK; - #ifdef HAVE_MENU if (menu_is_alive) return; #endif memset(handle->keys, 0, sizeof(handle->keys)); - i = 0; - if (device == RETRO_DEVICE_KEYBOARD) + + for (i = 0; i < 2; i++) { - for (i = 0; i < MAX_USERS; i++) + device = settings->uints.input_libretro_device[i]; + device &= RETRO_DEVICE_MASK; + + if (device == RETRO_DEVICE_KEYBOARD) { for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) { if (j < RETROK_LAST) { - if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && + if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) { MAPPER_SET_KEY (handle, @@ -129,7 +127,7 @@ void input_mapper_poll(input_mapper_t *handle) } else { - if (key_event[j] == false && + if (key_event[j] == false && settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) { input_keyboard_event(false, @@ -140,71 +138,37 @@ void input_mapper_poll(input_mapper_t *handle) } } } - } - if (device == RETRO_DEVICE_JOYPAD) - { - input_keys_pressed(settings, ¤t_input); - BIT256_CLEAR_ALL(handle->buttons); - for (i = 0; i < MAX_USERS; i++) + if (device == RETRO_DEVICE_JOYPAD) { /* this loop iterates on all users and all buttons, and checks if a pressed button is assigned to any other button than the default one, then it sets the bit on the mapper input bitmap, later on the original input is cleared in input_state */ + BIT256_CLEAR_ALL(handle->buttons[i]); + input_get_state_for_port(settings, i, ¤t_input); for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) { current_button_value = BIT256_GET(current_input, j); remap_button = settings->uints.input_remap_ids[i][j]; if (current_button_value == 1 && j != remap_button && - remap_button != RARCH_UNMAPPED) - BIT256_SET(handle->buttons, remap_button); + remap_button != RARCH_UNMAPPED) + BIT256_SET(handle->buttons[i], remap_button); } #if 0 /* --CURRENTLY NOT IMPLEMENTED-- this loop should iterate on all users and all analog stick axes and if the axes are moved and is assigned to a button it should set the bit on the mapper input bitmap. - Once implemented we should make sure to clear the original analog + Once implemented we should make sure to clear the original analog stick input in input_state in input_driver.c */ for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_CUSTOM_BIND_LIST_END; j++) - { - - } + { } #endif } } - if (device == RETRO_DEVICE_ANALOG) - { - input_keys_pressed(settings, ¤t_input); - BIT256_CLEAR_ALL(handle->buttons); - for (i = 0; i < MAX_USERS; i++) - { - /* this loop iterates on all users and all buttons, and checks if a pressed button - is assigned to any other button than the default one, then it sets the bit on the - mapper input bitmap, later on the original input is cleared in input_state */ - for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) - { - current_button_value = BIT256_GET(current_input, j); - remap_button = settings->uints.input_remap_ids[i][j]; - if (current_button_value == 1 && j != remap_button && - remap_button != RARCH_UNMAPPED) - BIT256_SET(handle->buttons, remap_button); - } -#if 0 - /* --CURRENTLY NOT IMPLEMENTED-- - this loop should iterate on all users and all analog stick axes and if the axes are - moved and is assigned to a button or another stick, it should set the bit on the - mapper input bitmap. Once implemented we should make sure to clear the original analog - stick input in input_state in input_driver.c */ - for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_CUSTOM_BIND_LIST_END; j++) - { - } -#endif - } - } } void input_mapper_state( @@ -221,7 +185,7 @@ void input_mapper_state( switch (device) { case RETRO_DEVICE_JOYPAD: - if (input_mapper_button_pressed(handle, id)) + if (input_mapper_button_pressed(handle, port, id)) *ret = 1; break; case RETRO_DEVICE_KEYBOARD: From 1fa28f0e31449cd42fdba2da38adf520aefe3693 Mon Sep 17 00:00:00 2001 From: radius Date: Tue, 3 Apr 2018 22:39:34 -0500 Subject: [PATCH 167/517] remap-redux part2: let's use this function instead, add logging --- input/input_driver.c | 9 ++++----- input/input_mapper.c | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 14cd308b0d..94f6a009eb 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1140,16 +1140,15 @@ void input_get_state_for_port(void *data, unsigned port, retro_bits_t* p_new_sta joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx]; joypad_info.axis_threshold = input_driver_axis_threshold; - input_driver_block_libretro_input = false; - input_driver_block_hotkey = false; - for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) { bool bit_pressed = false; - if (binds[i].valid && current_input->input_state(current_input_data, - joypad_info, &binds, port, RETRO_DEVICE_JOYPAD, 0, i)) + if (input_driver_input_state(joypad_info, &binds, port, RETRO_DEVICE_JOYPAD, 0, i) == 1) + { + RARCH_LOG("button pressed port: %d id: %d\n", port, i); bit_pressed = true; + } if (bit_pressed) BIT256_SET_PTR(p_new_state, i); diff --git a/input/input_mapper.c b/input/input_mapper.c index e7853d5141..a9c5703fec 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -144,7 +144,7 @@ void input_mapper_poll(input_mapper_t *handle) is assigned to any other button than the default one, then it sets the bit on the mapper input bitmap, later on the original input is cleared in input_state */ BIT256_CLEAR_ALL(handle->buttons[i]); - input_get_state_for_port(settings, i, ¤t_input); + input_get_state_for_port(settings, 0, ¤t_input); for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) { From 4260423e484c1f2d0e72aa61a54ab746239b8460 Mon Sep 17 00:00:00 2001 From: radius Date: Tue, 3 Apr 2018 22:57:33 -0500 Subject: [PATCH 168/517] remap-redux part2: let's use this function instead, add logging --- input/input_mapper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index a9c5703fec..e7853d5141 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -144,7 +144,7 @@ void input_mapper_poll(input_mapper_t *handle) is assigned to any other button than the default one, then it sets the bit on the mapper input bitmap, later on the original input is cleared in input_state */ BIT256_CLEAR_ALL(handle->buttons[i]); - input_get_state_for_port(settings, 0, ¤t_input); + input_get_state_for_port(settings, i, ¤t_input); for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) { From fa3dfd5f6334b87796910cab74fe04da056575df Mon Sep 17 00:00:00 2001 From: radius Date: Wed, 4 Apr 2018 12:22:07 -0500 Subject: [PATCH 169/517] remap-redux part2: finally user 2 mapping works! --- .vscode/launch.json | 20 ++++++++++++++++++++ input/input_driver.c | 7 +------ input/input_mapper.c | 3 +-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 4880045ed9..e7552247cc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,26 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/retroarch.exe", + "args": ["-v"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, { "name": "(gdb) Attach", "type": "cppdbg", diff --git a/input/input_driver.c b/input/input_driver.c index 94f6a009eb..defdd62c2d 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1132,8 +1132,6 @@ void input_get_state_for_port(void *data, unsigned port, retro_bits_t* p_new_sta unsigned i; rarch_joypad_info_t joypad_info; settings_t *settings = (settings_t*)data; - const struct retro_keybind *binds = input_config_binds[port]; - BIT256_CLEAR_ALL_PTR(p_new_state); joypad_info.joy_idx = settings->uints.input_joypad_map[port]; @@ -1144,11 +1142,8 @@ void input_get_state_for_port(void *data, unsigned port, retro_bits_t* p_new_sta { bool bit_pressed = false; - if (input_driver_input_state(joypad_info, &binds, port, RETRO_DEVICE_JOYPAD, 0, i) == 1) - { - RARCH_LOG("button pressed port: %d id: %d\n", port, i); + if (input_driver_input_state(joypad_info, libretro_input_binds, port, RETRO_DEVICE_JOYPAD, 0, i) != 0) bit_pressed = true; - } if (bit_pressed) BIT256_SET_PTR(p_new_state, i); diff --git a/input/input_mapper.c b/input/input_mapper.c index e7853d5141..3239cf5600 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -104,11 +104,10 @@ void input_mapper_poll(input_mapper_t *handle) memset(handle->keys, 0, sizeof(handle->keys)); - for (i = 0; i < 2; i++) + for (i = 0; i < max_users; i++) { device = settings->uints.input_libretro_device[i]; device &= RETRO_DEVICE_MASK; - if (device == RETRO_DEVICE_KEYBOARD) { for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) From 3792a5e502c98cbdc7b6f40de9cc45516466df2a Mon Sep 17 00:00:00 2001 From: radius Date: Wed, 4 Apr 2018 21:33:03 -0500 Subject: [PATCH 170/517] remap-redux part2: cleanup --- Makefile.common | 4 +--- Makefile.msvc | 1 - Makefile.vita | 1 - Makefile.wiiu | 1 - configuration.c | 6 ------ griffin/griffin.c | 2 -- input/input_driver.c | 21 ++++----------------- input/input_mapper.c | 13 ++++++------- input/input_mapper.h | 2 +- menu/cbs/menu_cbs_get_value.c | 4 ---- menu/cbs/menu_cbs_left.c | 4 ---- menu/cbs/menu_cbs_right.c | 4 ---- menu/cbs/menu_cbs_select.c | 4 ---- menu/cbs/menu_cbs_sublabel.c | 2 -- menu/menu_displaylist.c | 2 -- qb/config.libs.sh | 1 - qb/config.params.sh | 1 - 17 files changed, 12 insertions(+), 61 deletions(-) diff --git a/Makefile.common b/Makefile.common index 2e0d9b61ae..b12ea01505 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1605,9 +1605,7 @@ ifeq ($(HAVE_NETWORKING), 1) $(LIBRETRO_COMM_DIR)/utils/md5.o endif - ifeq ($(HAVE_KEYMAPPER), 1) - OBJ += input/input_mapper.o - endif + OBJ += input/input_mapper.o ifeq ($(HAVE_NETWORKGAMEPAD), 1) OBJ += input/input_remote.o \ diff --git a/Makefile.msvc b/Makefile.msvc index 77d02fc9fa..48405fd5c2 100644 --- a/Makefile.msvc +++ b/Makefile.msvc @@ -53,7 +53,6 @@ HAVE_NETWORK_CMD := 1 HAVE_OVERLAY := 1 HAVE_LANGEXTRA := 1 HAVE_CHEEVOS := 1 -HAVE_KEYMAPPER := 1 HAVE_SHADERPIPELINE := 1 HAVE_IMAGEVIEWER := 1 diff --git a/Makefile.vita b/Makefile.vita index efa4646cfb..5645c9cabd 100644 --- a/Makefile.vita +++ b/Makefile.vita @@ -33,7 +33,6 @@ else HAVE_ZLIB := 1 HAVE_7ZIP := 1 HAVE_VITA2D := 1 - HAVE_KEYMAPPER := 1 HAVE_NETWORKING := 1 HAVE_SOCKET_LEGACY := 1 HAVE_MENU := 1 diff --git a/Makefile.wiiu b/Makefile.wiiu index 5b2d3625a9..bcba3cd881 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -133,7 +133,6 @@ endif HAVE_ZLIB = 1 HAVE_7ZIP = 1 HAVE_BUILTINZLIB = 1 - HAVE_KEYMAPPER = 1 HAVE_LIBRETRODB = 1 HAVE_ZARCH = 0 HAVE_MATERIALUI = 1 diff --git a/configuration.c b/configuration.c index 9f0eed8a04..beb10a34d2 100644 --- a/configuration.c +++ b/configuration.c @@ -1358,9 +1358,6 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, #ifdef HAVE_NETWORKGAMEPAD SETTING_BOOL("network_remote_enable", &settings->bools.network_remote_enable, false, false /* TODO */, false); #endif -#ifdef HAVE_KEYMAPPER - SETTING_BOOL("keymapper_enable", &settings->bools.keymapper_enable, true, true /* TODO */, false); -#endif #ifdef HAVE_NETWORKING SETTING_BOOL("netplay_nat_traversal", &settings->bools.netplay_nat_traversal, true, true, false); #endif @@ -1465,9 +1462,6 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, #ifdef HAVE_NETWORKGAMEPAD SETTING_UINT("network_remote_base_port", &settings->uints.network_remote_base_port, true, network_remote_base_port, false); #endif -#ifdef HAVE_KEYMAPPER - SETTING_UINT("keymapper_port", &settings->uints.keymapper_port, true, 0, false); -#endif #ifdef GEKKO SETTING_UINT("video_viwidth", &settings->uints.video_viwidth, true, video_viwidth, false); #endif diff --git a/griffin/griffin.c b/griffin/griffin.c index e92b9da779..b9720bfa45 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1242,9 +1242,7 @@ MENU #include "../cores/libretro-net-retropad/net_retropad_core.c" #endif -#ifdef HAVE_KEYMAPPER #include "../input/input_mapper.c" -#endif #include "../command.c" diff --git a/input/input_driver.c b/input/input_driver.c index defdd62c2d..70a7d0340d 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -34,9 +34,7 @@ #include "input_remote.h" #endif -#ifdef HAVE_KEYMAPPER #include "input_mapper.h" -#endif #include "input_driver.h" #include "input_keymaps.h" @@ -377,9 +375,7 @@ static command_t *input_driver_command = NULL; #ifdef HAVE_NETWORKGAMEPAD static input_remote_t *input_driver_remote = NULL; #endif -#ifdef HAVE_KEYMAPPER static input_mapper_t *input_driver_mapper = NULL; -#endif static const input_driver_t *current_input = NULL; static void *current_input_data = NULL; static bool input_driver_block_hotkey = false; @@ -605,10 +601,8 @@ void input_poll(void) input_driver_axis_threshold); #endif -#ifdef HAVE_KEYMAPPER - if (input_driver_mapper) + if (settings->bools.input_remap_binds_enable && input_driver_mapper) input_mapper_poll(input_driver_mapper); -#endif #ifdef HAVE_COMMAND if (input_driver_command) @@ -692,11 +686,9 @@ int16_t input_state(unsigned port, unsigned device, } } -#ifdef HAVE_KEYMAPPER - if (input_driver_mapper) + if (settings->bools.input_remap_binds_enable && input_driver_mapper) input_mapper_state(input_driver_mapper, &res, port, device, idx, id); -#endif #ifdef HAVE_OVERLAY if (overlay_ptr) @@ -1368,11 +1360,9 @@ void input_driver_deinit_remote(void) void input_driver_deinit_mapper(void) { -#ifdef HAVE_KEYMAPPER if (input_driver_mapper) input_mapper_free(input_driver_mapper); input_driver_mapper = NULL; -#endif } bool input_driver_init_remote(void) @@ -1397,20 +1387,17 @@ bool input_driver_init_remote(void) bool input_driver_init_mapper(void) { -#ifdef HAVE_KEYMAPPER settings_t *settings = config_get_ptr(); - if (!settings->bools.keymapper_enable) + if (!settings->bools.input_remap_binds_enable) return false; - input_driver_mapper = input_mapper_new( - settings->uints.keymapper_port); + input_driver_mapper = input_mapper_new(); if (input_driver_mapper) return true; RARCH_ERR("Failed to initialize input mapper.\n"); -#endif return false; } diff --git a/input/input_mapper.c b/input/input_mapper.c index 3239cf5600..93a4295fa9 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -58,7 +58,7 @@ struct input_mapper retro_bits_t buttons[MAX_USERS]; }; -input_mapper_t *input_mapper_new(uint16_t port) +input_mapper_t *input_mapper_new(void) { input_mapper_t* handle = (input_mapper_t*) calloc(1, sizeof(*handle)); @@ -76,8 +76,6 @@ void input_mapper_free(input_mapper_t *handle) free (handle); } -bool flag = false; - bool input_mapper_button_pressed(input_mapper_t *handle, unsigned port, unsigned id) { return BIT256_GET(handle->buttons[port], id); @@ -108,6 +106,8 @@ void input_mapper_poll(input_mapper_t *handle) { device = settings->uints.input_libretro_device[i]; device &= RETRO_DEVICE_MASK; + + /* keyboard to gamepad remapping */ if (device == RETRO_DEVICE_KEYBOARD) { for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) @@ -137,7 +137,9 @@ void input_mapper_poll(input_mapper_t *handle) } } } - if (device == RETRO_DEVICE_JOYPAD) + + /* gamepad remapping */ + if (device == RETRO_DEVICE_JOYPAD || device == RETRO_DEVICE_ANALOG) { /* this loop iterates on all users and all buttons, and checks if a pressed button is assigned to any other button than the default one, then it sets the bit on the @@ -165,9 +167,6 @@ void input_mapper_poll(input_mapper_t *handle) #endif } } - - - } void input_mapper_state( diff --git a/input/input_mapper.h b/input/input_mapper.h index 5f2b91e51b..21741d008e 100644 --- a/input/input_mapper.h +++ b/input/input_mapper.h @@ -31,7 +31,7 @@ RETRO_BEGIN_DECLS typedef struct input_mapper input_mapper_t; -input_mapper_t *input_mapper_new(uint16_t port); +input_mapper_t *input_mapper_new(void); void input_mapper_free(input_mapper_t *handle); diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index f6bcfd117e..54e39b2bcd 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -581,7 +581,6 @@ static void menu_action_setting_disp_set_label_input_desc( strlcpy(s2, path, len2); } -#ifdef HAVE_KEYMAPPER static void menu_action_setting_disp_set_label_input_desc_kbd( file_list_t* list, unsigned *w, unsigned type, unsigned i, @@ -627,7 +626,6 @@ static void menu_action_setting_disp_set_label_input_desc_kbd( *w = 19; strlcpy(s2, path, len2); } -#endif static void menu_action_setting_disp_set_label_cheat( file_list_t* list, @@ -2076,14 +2074,12 @@ static int menu_cbs_init_bind_get_string_representation_compare_type( BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_libretro_perf_counters); } -#ifdef HAVE_KEYMAPPER else if (type >= MENU_SETTINGS_INPUT_DESC_KBD_BEGIN && type <= MENU_SETTINGS_INPUT_DESC_KBD_END) { BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_input_desc_kbd); } -#endif else { switch (type) diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 98e96b721b..37b0d51270 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -124,7 +124,6 @@ static int action_left_input_desc(unsigned type, const char *label, return 0; } -#ifdef HAVE_KEYMAPPER static int action_left_input_desc_kbd(unsigned type, const char *label, bool wraparound) { @@ -161,7 +160,6 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, return 0; } -#endif static int action_left_scroll(unsigned type, const char *label, bool wraparound) @@ -633,13 +631,11 @@ static int menu_cbs_init_bind_left_compare_type(menu_file_list_cbs_t *cbs, { BIND_ACTION_LEFT(cbs, action_left_input_desc); } -#ifdef HAVE_KEYMAPPER else if (type >= MENU_SETTINGS_INPUT_DESC_KBD_BEGIN && type <= MENU_SETTINGS_INPUT_DESC_KBD_END) { BIND_ACTION_LEFT(cbs, action_left_input_desc_kbd); } -#endif else if ((type >= MENU_SETTINGS_PLAYLIST_ASSOCIATION_START)) { BIND_ACTION_LEFT(cbs, playlist_association_left); diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index ee6cbd85b2..8451f51029 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -101,7 +101,6 @@ int action_right_cheat(unsigned type, const char *label, wraparound); } -#ifdef HAVE_KEYMAPPER int action_right_input_desc_kbd(unsigned type, const char *label, bool wraparound) { @@ -139,7 +138,6 @@ int action_right_input_desc_kbd(unsigned type, const char *label, return 0; } -#endif /* fix-me: incomplete, lacks error checking */ int action_right_input_desc(unsigned type, const char *label, @@ -503,13 +501,11 @@ static int menu_cbs_init_bind_right_compare_type(menu_file_list_cbs_t *cbs, { BIND_ACTION_RIGHT(cbs, action_right_input_desc); } -#ifdef HAVE_KEYMAPPER else if (type >= MENU_SETTINGS_INPUT_DESC_KBD_BEGIN && type <= MENU_SETTINGS_INPUT_DESC_KBD_END) { BIND_ACTION_RIGHT(cbs, action_right_input_desc_kbd); } -#endif else if ((type >= MENU_SETTINGS_PLAYLIST_ASSOCIATION_START)) { BIND_ACTION_RIGHT(cbs, playlist_association_right); diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 0246455d0d..7a6bc5e047 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -146,14 +146,12 @@ static int action_select_input_desc(const char *path, const char *label, unsigne return action_right_input_desc(type, label, true); } -#ifdef HAVE_KEYMAPPER static int action_select_input_desc_kbd(const char *path, const char *label, unsigned type, size_t idx) { return action_right_input_desc_kbd(type, label, true); } -#endif #ifdef HAVE_NETWORKING static int action_select_netplay_connect_room(const char *path, @@ -223,13 +221,11 @@ static int menu_cbs_init_bind_select_compare_type( { BIND_ACTION_SELECT(cbs, action_select_input_desc); } -#ifdef HAVE_KEYMAPPER else if (type >= MENU_SETTINGS_INPUT_DESC_KBD_BEGIN && type <= MENU_SETTINGS_INPUT_DESC_KBD_END) { BIND_ACTION_SELECT(cbs, action_select_input_desc_kbd); } -#endif else { diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 3facf11794..735b2911b7 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -533,13 +533,11 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_generic); -#ifdef HAVE_KEYMAPPER if (type >= MENU_SETTINGS_INPUT_DESC_KBD_BEGIN && type <= MENU_SETTINGS_INPUT_DESC_KBD_END) { BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_remap_kbd_sublabel); } -#endif if (type >= MENU_SETTINGS_INPUT_DESC_BEGIN && type <= MENU_SETTINGS_INPUT_DESC_END) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 106c16f445..44df5c5cf4 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3282,7 +3282,6 @@ static int menu_displaylist_parse_options_remappings( } } } - #ifdef HAVE_KEYMAPPER if (system) { settings_t *settings = config_get_ptr(); @@ -3325,7 +3324,6 @@ static int menu_displaylist_parse_options_remappings( } } } - #endif return 0; } diff --git a/qb/config.libs.sh b/qb/config.libs.sh index e0c3382d5b..701d8c162d 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -210,7 +210,6 @@ if [ "$HAVE_NETWORKING" = 'yes' ]; then check_lib '' MINIUPNPC '-lminiupnpc' else die : 'Warning: All networking features have been disabled.' - HAVE_KEYMAPPER='no' HAVE_NETWORK_CMD='no' HAVE_NETWORKGAMEPAD='no' HAVE_CHEEVOS='no' diff --git a/qb/config.params.sh b/qb/config.params.sh index 94135f94f6..f3899a3403 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -31,7 +31,6 @@ HAVE_SSA=auto # SSA/ASS for FFmpeg subtitle support HAVE_DYLIB=auto # Dynamic loading support HAVE_NETWORKING=auto # Networking features (recommended) HAVE_NETWORKGAMEPAD=auto # Networked game pad (plus baked-in core) -HAVE_KEYMAPPER=yes # Networked game pad (plus baked-in core) C89_NETWORKGAMEPAD=no HAVE_MINIUPNPC=auto # Mini UPnP client library (for NAT traversal) HAVE_BUILTINMINIUPNPC=yes # Bake in Mini UPnP client library (for NAT traversal) From 06860bf704c328917602612a37a707821e2ce2da Mon Sep 17 00:00:00 2001 From: radius Date: Wed, 4 Apr 2018 22:27:25 -0500 Subject: [PATCH 171/517] remap-redux part2: rewrite keymapper to work like gamepad mapper --- input/input_mapper.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index 93a4295fa9..805297a3b9 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -110,27 +110,29 @@ void input_mapper_poll(input_mapper_t *handle) /* keyboard to gamepad remapping */ if (device == RETRO_DEVICE_KEYBOARD) { + input_get_state_for_port(settings, i, ¤t_input); for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) { - if (j < RETROK_LAST) { - if (input_state(i, RETRO_DEVICE_JOYPAD, 0, j) && - settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) + current_button_value = BIT256_GET(current_input, j); + remap_button = settings->uints.input_keymapper_ids[i][j]; + if (current_button_value == 1 && j != remap_button && + remap_button != RETROK_UNKNOWN) { MAPPER_SET_KEY (handle, - settings->uints.input_keymapper_ids[i][j]); + remap_button); input_keyboard_event(true, - settings->uints.input_keymapper_ids[i][j], + remap_button, 0, 0, RETRO_DEVICE_KEYBOARD); key_event[j] = true; } else { if (key_event[j] == false && - settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) + remap_button != RETROK_UNKNOWN) { input_keyboard_event(false, - settings->uints.input_keymapper_ids[i][j], + remap_button, 0, 0, RETRO_DEVICE_KEYBOARD); } } From f662d9f65fc593b4d526e7d3f7212452214276b5 Mon Sep 17 00:00:00 2001 From: radius Date: Sat, 7 Apr 2018 23:43:41 -0500 Subject: [PATCH 172/517] remap-redux part2: start adding analog remapping --- input/input_remapping.c | 93 ++++++++++++++++++++--------------- menu/cbs/menu_cbs_get_value.c | 20 ++++++-- menu/cbs/menu_cbs_left.c | 4 +- menu/cbs/menu_cbs_right.c | 4 +- menu/menu_displaylist.c | 2 +- 5 files changed, 72 insertions(+), 51 deletions(-) diff --git a/input/input_remapping.c b/input/input_remapping.c index 370e31a6a9..15a34c1d7d 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -53,64 +53,70 @@ bool input_remapping_load_file(void *data, const char *path) for (i = 0; i < MAX_USERS; i++) { - char s1[64], s2[64]; + char s1[64], s2[64], s3[64]; + char btn_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; char key_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char keymapper_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char key_strings[RARCH_FIRST_CUSTOM_BIND + 4][128] = - { "b", "y", "select", "start", + char stk_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; + + char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = { + "b", "y", "select", "start", "up", "down", "left", "right", "a", "x", "l", "r", "l2", "r2", - "l3", "r3", "l_x", "l_y", "r_x", "r_y" }; + "l3", "r3", "l_x+", "l_x-", "l_y+", "l_y-", "r_x+", "r_x-", "r_y+", "r_y-" }; old_analog_dpad_mode[i] = settings->uints.input_analog_dpad_mode[i]; old_libretro_device[i] = settings->uints.input_libretro_device[i]; s1[0] = '\0'; s2[0] = '\0'; + s3[0] = '\0'; snprintf(s1, sizeof(s1), "input_player%u_btn", i + 1); snprintf(s2, sizeof(s2), "input_player%u_key", i + 1); + snprintf(s3, sizeof(s3), "input_player%u_stk", i + 1); - for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 4; j++) + for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) { int btn_remap = -1; - fill_pathname_join_delim(key_ident[j], s1, - key_strings[j], '_', sizeof(key_ident[j])); - fill_pathname_join_delim(keymapper_ident[j], s2, - key_strings[j], '_', sizeof(key_ident[j])); + fill_pathname_join_delim(btn_ident[j], s1, + key_strings[j], '_', sizeof(btn_ident[j])); + fill_pathname_join_delim(key_ident[j], s2, + key_strings[j], '_', sizeof(btn_ident[j])); - if (config_get_int(conf, key_ident[j], &btn_remap) + if (config_get_int(conf, btn_ident[j], &btn_remap) && (btn_remap < RARCH_FIRST_CUSTOM_BIND && btn_remap != -1)) settings->uints.input_remap_ids[i][j] = btn_remap; - else if (config_get_int(conf, key_ident[j], &btn_remap) + else if (config_get_int(conf, btn_ident[j], &btn_remap) && (btn_remap == -1)) settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; int key_remap = -1; - if (config_get_int(conf, keymapper_ident[j], &key_remap)) + if (config_get_int(conf, key_ident[j], &key_remap)) settings->uints.input_keymapper_ids[i][j] = key_remap; else settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; - - } - for (j = 0; j < 4; j++) + for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) { - int btn_remap = -1; + int stk_remap = -1; - snprintf(key_ident[RARCH_FIRST_CUSTOM_BIND + j], - sizeof(key_ident[RARCH_FIRST_CUSTOM_BIND + j]), + fill_pathname_join_delim(stk_ident[j], s3, + key_strings[j], '$', sizeof(stk_ident[j])); + + snprintf(stk_ident[j], + sizeof(stk_ident[j]), "%s_%s", - s1, - key_strings[RARCH_FIRST_CUSTOM_BIND + j]); + s3, + key_strings[j]); - if (config_get_int(conf, key_ident[RARCH_FIRST_CUSTOM_BIND + j], - &btn_remap) && (btn_remap < 4)) - settings->uints.input_remap_ids[i][RARCH_FIRST_CUSTOM_BIND + j] = - btn_remap; + if (config_get_int(conf, stk_ident[j], + &stk_remap)) + settings->uints.input_remap_ids[i][j] = + stk_remap; + RARCH_LOG("stk_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]); } snprintf(s1, sizeof(s1), "input_player%u_analog_dpad_mode", i + 1); @@ -167,50 +173,55 @@ bool input_remapping_save_file(const char *path) for (i = 0; i < max_users; i++) { - char s1[64], s2[64]; + char s1[64], s2[64], s3[64]; + char btn_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; char key_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char keymapper_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char key_strings[RARCH_FIRST_CUSTOM_BIND + 4][128] = { + char stk_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; + + char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = { "b", "y", "select", "start", "up", "down", "left", "right", "a", "x", "l", "r", "l2", "r2", - "l3", "r3", "l_x", "l_y", "r_x", "r_y" }; + "l3", "r3", "l_x+", "l_x-", "l_y+", "l_y-", "r_x+", "r_x-", "r_y+", "r_y-" }; s1[0] = '\0'; s2[0] = '\0'; snprintf(s1, sizeof(s1), "input_player%u_btn", i + 1); snprintf(s2, sizeof(s2), "input_player%u_key", i + 1); + snprintf(s3, sizeof(s1), "input_player%u_stk", i + 1); for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 4; j++) { - fill_pathname_join_delim(key_ident[j], s1, - key_strings[j], '_', sizeof(key_ident[j])); - fill_pathname_join_delim(keymapper_ident[j], s2, - key_strings[j], '_', sizeof(key_ident[j])); + fill_pathname_join_delim(btn_ident[j], s1, + key_strings[j], '_', sizeof(btn_ident[j])); + fill_pathname_join_delim(key_ident[j], s2, + key_strings[j], '_', sizeof(btn_ident[j])); /* only save values that have been modified */ if(j < RARCH_FIRST_CUSTOM_BIND) { if(settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] != RARCH_UNMAPPED) - config_set_int(conf, key_ident[j], settings->uints.input_remap_ids[i][j]); + config_set_int(conf, btn_ident[j], settings->uints.input_remap_ids[i][j]); else if (settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] == RARCH_UNMAPPED) - config_set_int(conf, key_ident[j], -1); + config_set_int(conf, btn_ident[j], -1); else - config_unset(conf,key_ident[j]); + config_unset(conf,btn_ident[j]); if (settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) - config_set_int(conf, keymapper_ident[j], + config_set_int(conf, key_ident[j], settings->uints.input_keymapper_ids[i][j]); } else { + fill_pathname_join_delim(stk_ident[j], s3, + key_strings[j], '_', sizeof(stk_ident[j])); if(settings->uints.input_remap_ids[i][j] != j - RARCH_FIRST_CUSTOM_BIND) - config_set_int(conf, key_ident[j], + config_set_int(conf, stk_ident[j], settings->uints.input_remap_ids[i][j]); else - config_unset(conf,key_ident[j]); + config_unset(conf,btn_ident[j]); } } snprintf(s1, sizeof(s1), "input_libretro_device_p%u", i + 1); @@ -274,8 +285,8 @@ void input_remapping_set_defaults(bool deinit) settings->uints.input_remap_ids[i][j] = keybind->id; settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; } - for (j = 0; j < 4; j++) - settings->uints.input_remap_ids[i][RARCH_FIRST_CUSTOM_BIND + j] = j; + for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) + settings->uints.input_remap_ids[i][j] = j; if (old_analog_dpad_mode[i]) settings->uints.input_analog_dpad_mode[i] = old_analog_dpad_mode[i]; diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 54e39b2bcd..215162cf77 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -550,6 +550,7 @@ static void menu_action_setting_disp_set_label_input_desc( rarch_system_info_t *system = runloop_get_system_info(); settings_t *settings = config_get_ptr(); const char* descriptor = NULL; + char buf[256]; unsigned btn_idx, user_idx, remap_idx; @@ -561,18 +562,27 @@ static void menu_action_setting_disp_set_label_input_desc( remap_idx = settings->uints.input_remap_ids[user_idx][btn_idx]; - +/* if (remap_idx == RARCH_UNMAPPED) settings->uints.input_remap_ids[user_idx][btn_idx] = RARCH_UNMAPPED; - +*/ if (!system) return; - if (btn_idx < RARCH_FIRST_CUSTOM_BIND) - descriptor = system->input_desc_btn[user_idx][remap_idx]; + descriptor = system->input_desc_btn[user_idx][remap_idx]; - if (!string_is_empty(descriptor)) + if (!string_is_empty(descriptor) && remap_idx < RARCH_FIRST_CUSTOM_BIND) strlcpy(s, descriptor, len); + else if (!string_is_empty(descriptor) && remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx % 2 == 0) + { + snprintf(buf, sizeof(buf), "%s %c", descriptor, '+'); + strlcpy(s, buf, len); + } + else if (!string_is_empty(descriptor) && remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx % 2 != 0) + { + snprintf(buf, sizeof(buf), "%s %c", descriptor, '-'); + strlcpy(s, buf, len); + } else strlcpy(s, "---", len); diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 37b0d51270..53115b011d 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -117,8 +117,8 @@ static int action_left_input_desc(unsigned type, const char *label, /* skip the not used buttons (unless they are at the end by calling the right desc function recursively also skip all the axes until analog remapping is implemented */ - if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) || - (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)) + if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) /*|| + (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)*/) action_left_input_desc(type, label, wraparound); return 0; diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 8451f51029..0f2fa245a6 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -164,8 +164,8 @@ int action_right_input_desc(unsigned type, const char *label, /* skip the not used buttons (unless they are at the end by calling the right desc function recursively also skip all the axes until analog remapping is implemented */ - if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) || - (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)) + if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) /*|| + (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)*/) action_right_input_desc(type, label, wraparound); #if 0 diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 44df5c5cf4..d8a276e72a 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3250,7 +3250,7 @@ static int menu_displaylist_parse_options_remappings( if (device == RETRO_DEVICE_JOYPAD || device == RETRO_DEVICE_ANALOG) { /* change to RARCH_FIRST_CUSTOM_BIND + 8 once analog remapping is implemented */ - for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) + for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND + 8; retro_id++) { char desc_label[64]; unsigned user = p + 1; From 0ed9f055710aa4e3c497817c92e756ca149238af Mon Sep 17 00:00:00 2001 From: radius Date: Sun, 8 Apr 2018 00:44:05 -0500 Subject: [PATCH 173/517] remap-redux part2: - remapping analogs to buttons works 100% - remapping analogs to other analogs still messed up for some reason - need to reset input of the original axis in input_driver.c still --- input/input_driver.c | 32 ++++++++++++++++++- input/input_mapper.c | 31 ++++++++++++++---- libretro-common/include/retro_miscellaneous.h | 2 ++ 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 70a7d0340d..bad2559b6d 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1121,7 +1121,8 @@ void input_keys_pressed(void *data, retro_bits_t* p_new_state) void input_get_state_for_port(void *data, unsigned port, retro_bits_t* p_new_state) { - unsigned i; + unsigned i, j; + int16_t val; rarch_joypad_info_t joypad_info; settings_t *settings = (settings_t*)data; BIT256_CLEAR_ALL_PTR(p_new_state); @@ -1140,6 +1141,35 @@ void input_get_state_for_port(void *data, unsigned port, retro_bits_t* p_new_sta if (bit_pressed) BIT256_SET_PTR(p_new_state, i); } + + /* left-stick x */ + val = input_joypad_analog(input_driver_get_joypad_driver(), joypad_info, port, 0, 0, libretro_input_binds[port]); + if (val >= 0) + p_new_state->analogs[0] = val; + else + p_new_state->analogs[1] = val; + + /* left-stick y */ + val = input_joypad_analog(input_driver_get_joypad_driver(), joypad_info, port, 0, 1, libretro_input_binds[port]); + if (val >= 0) + p_new_state->analogs[2] = val; + else + p_new_state->analogs[3] = val; + + /* right-stick x */ + val = input_joypad_analog(input_driver_get_joypad_driver(), joypad_info, port, 1, 0, libretro_input_binds[port]); + if (val >= 0) + p_new_state->analogs[4] = val; + else + p_new_state->analogs[5] = val; + + /* right-stick y */ + val = input_joypad_analog(input_driver_get_joypad_driver(), joypad_info, port, 1, 1, libretro_input_binds[port]); + if (val >= 0) + p_new_state->analogs[6] = val; + else + p_new_state->analogs[7] = val; + } void *input_driver_get_data(void) diff --git a/input/input_mapper.c b/input/input_mapper.c index 805297a3b9..a467e6008f 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -51,7 +51,7 @@ struct input_mapper { /* Left X, Left Y, Right X, Right Y */ - int16_t analog[4]; + int16_t analog[MAX_USERS][8]; /* the whole keyboard state */ uint32_t keys[RETROK_LAST / 32 + 1]; /* This is a bitmask of (1 << key_bind_id). */ @@ -83,13 +83,14 @@ bool input_mapper_button_pressed(input_mapper_t *handle, unsigned port, unsigned void input_mapper_poll(input_mapper_t *handle) { - int i, j; + int i, j, k; settings_t *settings = config_get_ptr(); retro_bits_t current_input; unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); unsigned device = 0; unsigned current_button_value; - unsigned remap_button; + int16_t current_axis_value; + unsigned remap_button, remap_axis; bool key_event[RARCH_CUSTOM_BIND_LIST_END]; #ifdef HAVE_MENU bool menu_is_alive = menu_driver_is_alive(); @@ -157,15 +158,29 @@ void input_mapper_poll(input_mapper_t *handle) remap_button != RARCH_UNMAPPED) BIT256_SET(handle->buttons[i], remap_button); } -#if 0 +#if 1 /* --CURRENTLY NOT IMPLEMENTED-- this loop should iterate on all users and all analog stick axes and if the axes are moved and is assigned to a button it should set the bit on the mapper input bitmap. Once implemented we should make sure to clear the original analog stick input in input_state in input_driver.c */ - for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_CUSTOM_BIND_LIST_END; j++) - { } + for (j = 0; j < 8; j++) + { + handle->analog[i][j] = 0; + + k = j + RARCH_FIRST_CUSTOM_BIND; + current_axis_value = current_input.analogs[j]; + remap_axis = settings->uints.input_remap_ids[i][k]; + if (current_axis_value != 0 && k != remap_axis && remap_axis != RARCH_UNMAPPED) + { + if (remap_axis < RARCH_FIRST_CUSTOM_BIND) + BIT256_SET(handle->buttons[i], remap_axis); + else + handle->analog[i][remap_axis - RARCH_FIRST_CUSTOM_BIND] = current_axis_value; + } + + } #endif } } @@ -188,6 +203,10 @@ void input_mapper_state( if (input_mapper_button_pressed(handle, port, id)) *ret = 1; break; + case RETRO_DEVICE_ANALOG: + if (handle->analog[port][idx == 0 ? id : id / 2] != 0) + *ret = handle->analog[port][idx == 0 ? id : id / 2]; + break; case RETRO_DEVICE_KEYBOARD: if (id < RETROK_LAST) { diff --git a/libretro-common/include/retro_miscellaneous.h b/libretro-common/include/retro_miscellaneous.h index 23f9ef02ac..d39b51feca 100644 --- a/libretro-common/include/retro_miscellaneous.h +++ b/libretro-common/include/retro_miscellaneous.h @@ -153,6 +153,8 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count) typedef struct { uint32_t data[8]; + uint16_t analogs[8]; } retro_bits_t; + #endif From c608951ff890d81ee6e6934d871bf282d8dd827f Mon Sep 17 00:00:00 2001 From: radius Date: Sun, 8 Apr 2018 01:45:40 -0500 Subject: [PATCH 174/517] remap-redux part2: - remapping analogs to buttons works 100% - remapping to analogs needs the "new input rules written" based on the value and the new axis --- input/input_driver.c | 24 ++++++++++++++++++++++++ input/input_mapper.c | 18 ++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index bad2559b6d..35cfb2860b 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -663,6 +663,30 @@ int16_t input_state(unsigned port, unsigned device, break; case RETRO_DEVICE_ANALOG: if (idx < 2 && id < 2) + { + if (idx == 0) + { + if (id == 0 && settings->uints.input_remap_ids[port][16] != 16) + reset_state = true; + if (id == 0 && settings->uints.input_remap_ids[port][17] != 17) + reset_state = true; + if (id == 1 && settings->uints.input_remap_ids[port][18] != 18) + reset_state = true; + if (id == 1 && settings->uints.input_remap_ids[port][19] != 19) + reset_state = true; + } + if (idx == 1) + { + if (id == 0 && settings->uints.input_remap_ids[port][16] != 20) + reset_state = true; + if (id == 0 && settings->uints.input_remap_ids[port][17] != 21) + reset_state = true; + if (id == 1 && settings->uints.input_remap_ids[port][18] != 22) + reset_state = true; + if (id == 1 && settings->uints.input_remap_ids[port][19] != 23) + reset_state = true; + } + } break; } } diff --git a/input/input_mapper.c b/input/input_mapper.c index a467e6008f..930a757846 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -51,7 +51,8 @@ struct input_mapper { /* Left X, Left Y, Right X, Right Y */ - int16_t analog[MAX_USERS][8]; + int16_t analog_value[MAX_USERS][8]; + int new_axis[MAX_USERS][8]; /* the whole keyboard state */ uint32_t keys[RETROK_LAST / 32 + 1]; /* This is a bitmask of (1 << key_bind_id). */ @@ -148,6 +149,7 @@ void input_mapper_poll(input_mapper_t *handle) is assigned to any other button than the default one, then it sets the bit on the mapper input bitmap, later on the original input is cleared in input_state */ BIT256_CLEAR_ALL(handle->buttons[i]); + input_get_state_for_port(settings, i, ¤t_input); for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) @@ -167,17 +169,23 @@ void input_mapper_poll(input_mapper_t *handle) for (j = 0; j < 8; j++) { - handle->analog[i][j] = 0; - k = j + RARCH_FIRST_CUSTOM_BIND; current_axis_value = current_input.analogs[j]; remap_axis = settings->uints.input_remap_ids[i][k]; + if (current_axis_value != 0 && k != remap_axis && remap_axis != RARCH_UNMAPPED) { if (remap_axis < RARCH_FIRST_CUSTOM_BIND) + { BIT256_SET(handle->buttons[i], remap_axis); + //RARCH_LOG("axis %d remapped to button %d val %d\n", j, remap_axis, current_axis_value); + } else - handle->analog[i][remap_axis - RARCH_FIRST_CUSTOM_BIND] = current_axis_value; + { + handle->analog_value[i][remap_axis - RARCH_FIRST_CUSTOM_BIND] = current_axis_value; + handle->new_axis[i][remap_axis - RARCH_FIRST_CUSTOM_BIND] = remap_axis; + RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", j, k, remap_axis - RARCH_FIRST_CUSTOM_BIND, current_axis_value); + } } } @@ -204,8 +212,6 @@ void input_mapper_state( *ret = 1; break; case RETRO_DEVICE_ANALOG: - if (handle->analog[port][idx == 0 ? id : id / 2] != 0) - *ret = handle->analog[port][idx == 0 ? id : id / 2]; break; case RETRO_DEVICE_KEYBOARD: if (id < RETROK_LAST) From 72065aee0b629c9e87a624086303d35ec9610c10 Mon Sep 17 00:00:00 2001 From: radius Date: Sun, 8 Apr 2018 02:49:46 -0500 Subject: [PATCH 175/517] remap-redux part2: analog to analog input rules are working for left stick to left stick and left stick to right, for some reason not for right stick to right stick (inverting axes) or right stick to left... --- input/input_driver.c | 8 ++--- input/input_mapper.c | 76 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 35cfb2860b..3df9740e19 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -677,13 +677,13 @@ int16_t input_state(unsigned port, unsigned device, } if (idx == 1) { - if (id == 0 && settings->uints.input_remap_ids[port][16] != 20) + if (id == 0 && settings->uints.input_remap_ids[port][20] != 20) reset_state = true; - if (id == 0 && settings->uints.input_remap_ids[port][17] != 21) + if (id == 0 && settings->uints.input_remap_ids[port][21] != 21) reset_state = true; - if (id == 1 && settings->uints.input_remap_ids[port][18] != 22) + if (id == 1 && settings->uints.input_remap_ids[port][22] != 22) reset_state = true; - if (id == 1 && settings->uints.input_remap_ids[port][19] != 23) + if (id == 1 && settings->uints.input_remap_ids[port][23] != 23) reset_state = true; } } diff --git a/input/input_mapper.c b/input/input_mapper.c index 930a757846..add2d226be 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -52,7 +52,6 @@ struct input_mapper { /* Left X, Left Y, Right X, Right Y */ int16_t analog_value[MAX_USERS][8]; - int new_axis[MAX_USERS][8]; /* the whole keyboard state */ uint32_t keys[RETROK_LAST / 32 + 1]; /* This is a bitmask of (1 << key_bind_id). */ @@ -149,6 +148,8 @@ void input_mapper_poll(input_mapper_t *handle) is assigned to any other button than the default one, then it sets the bit on the mapper input bitmap, later on the original input is cleared in input_state */ BIT256_CLEAR_ALL(handle->buttons[i]); + for (j = 0; j < 8; j++) + handle->analog_value[i][j] = 0; input_get_state_for_port(settings, i, ¤t_input); @@ -170,6 +171,7 @@ void input_mapper_poll(input_mapper_t *handle) for (j = 0; j < 8; j++) { k = j + RARCH_FIRST_CUSTOM_BIND; + current_axis_value = current_input.analogs[j]; remap_axis = settings->uints.input_remap_ids[i][k]; @@ -178,13 +180,20 @@ void input_mapper_poll(input_mapper_t *handle) if (remap_axis < RARCH_FIRST_CUSTOM_BIND) { BIT256_SET(handle->buttons[i], remap_axis); - //RARCH_LOG("axis %d remapped to button %d val %d\n", j, remap_axis, current_axis_value); + /* RARCH_LOG("axis %d remapped to button %d val %d\n", j, remap_axis, current_axis_value); */ } else { - handle->analog_value[i][remap_axis - RARCH_FIRST_CUSTOM_BIND] = current_axis_value; - handle->new_axis[i][remap_axis - RARCH_FIRST_CUSTOM_BIND] = remap_axis; - RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", j, k, remap_axis - RARCH_FIRST_CUSTOM_BIND, current_axis_value); + int invert = 1; + /*if ((k == 16 && remap_axis == 17) || (k == 17 && remap_axis == 16) || + (k == 18 && remap_axis == 19) || (k == 19 && remap_axis == 18) || + (k == 20 && remap_axis == 21) || (k == 21 && remap_axis == 20) || + (k == 22 && remap_axis == 23) || (k == 23 && remap_axis == 22))*/ + if ((k % 2 == 0 && remap_axis % 2 != 0) || (k % 2 != 0 && remap_axis % 2 == 0)) + invert = -1; + + handle->analog_value[i][remap_axis - RARCH_FIRST_CUSTOM_BIND] = current_axis_value * invert; + /* RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", j, k, remap_axis - RARCH_FIRST_CUSTOM_BIND, current_axis_value); */ } } @@ -212,6 +221,63 @@ void input_mapper_state( *ret = 1; break; case RETRO_DEVICE_ANALOG: + { + int val = 0; + if (idx == 0) + { + if (id == 0) + { + if (handle->analog_value[port][0]) + val = handle->analog_value[port][0]; + else if (handle->analog_value[port][1]) + val = handle->analog_value[port][1]; + + if(handle->analog_value[port][0] || handle->analog_value[port][1]) + { + *ret = val; + } + } + if (id == 1) + { + if (handle->analog_value[port][2]) + val = handle->analog_value[port][2]; + else if (handle->analog_value[port][3]) + val = handle->analog_value[port][3]; + + if(handle->analog_value[port][2] || handle->analog_value[port][3]) + { + *ret = val; + } + } + if (idx == 1) + { + if (id == 0) + { + if (handle->analog_value[port][4]) + val = handle->analog_value[port][4]; + else if (handle->analog_value[port][5]) + val = handle->analog_value[port][5]; + + if(handle->analog_value[port][4] || handle->analog_value[port][5]) + { + *ret = val; + } + } + if (id == 1) + { + if (handle->analog_value[port][6]) + val = handle->analog_value[port][6]; + else if (handle->analog_value[port][7]) + val = handle->analog_value[port][7]; + + if(handle->analog_value[port][6] || handle->analog_value[port][7]) + { + *ret = val; + } + } + } + } + } break; case RETRO_DEVICE_KEYBOARD: if (id < RETROK_LAST) From f517ca3b56eeab569c405c9d967306985acb9729 Mon Sep 17 00:00:00 2001 From: radius Date: Sun, 8 Apr 2018 11:01:47 -0500 Subject: [PATCH 176/517] remap-redux part2: fix right stick --- input/input_mapper.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index add2d226be..130c8a71c2 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -249,31 +249,31 @@ void input_mapper_state( *ret = val; } } - if (idx == 1) + } + if (idx == 1) + { + if (id == 0) { - if (id == 0) - { - if (handle->analog_value[port][4]) - val = handle->analog_value[port][4]; - else if (handle->analog_value[port][5]) - val = handle->analog_value[port][5]; + if (handle->analog_value[port][4]) + val = handle->analog_value[port][4]; + else if (handle->analog_value[port][5]) + val = handle->analog_value[port][5]; - if(handle->analog_value[port][4] || handle->analog_value[port][5]) - { - *ret = val; - } + if(handle->analog_value[port][4] || handle->analog_value[port][5]) + { + *ret = val; } - if (id == 1) - { - if (handle->analog_value[port][6]) - val = handle->analog_value[port][6]; - else if (handle->analog_value[port][7]) - val = handle->analog_value[port][7]; + } + if (id == 1) + { + if (handle->analog_value[port][6]) + val = handle->analog_value[port][6]; + else if (handle->analog_value[port][7]) + val = handle->analog_value[port][7]; - if(handle->analog_value[port][6] || handle->analog_value[port][7]) - { - *ret = val; - } + if(handle->analog_value[port][6] || handle->analog_value[port][7]) + { + *ret = val; } } } From 2b9b82945947c187a149280d905ea8e4a17b3641 Mon Sep 17 00:00:00 2001 From: radius Date: Sun, 8 Apr 2018 12:12:45 -0500 Subject: [PATCH 177/517] remap-redux part2: fix saving, loading and set defaults --- input/input_mapper.c | 40 ++++++++++++++++++++++------------------ input/input_remapping.c | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index 130c8a71c2..67da220234 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -158,15 +158,21 @@ void input_mapper_poll(input_mapper_t *handle) current_button_value = BIT256_GET(current_input, j); remap_button = settings->uints.input_remap_ids[i][j]; if (current_button_value == 1 && j != remap_button && - remap_button != RARCH_UNMAPPED) + remap_button != RARCH_UNMAPPED && remap_button < RARCH_FIRST_CUSTOM_BIND) BIT256_SET(handle->buttons[i], remap_button); + else if (current_button_value == 1 && j != remap_button && + remap_button != RARCH_UNMAPPED && remap_button >= RARCH_FIRST_CUSTOM_BIND) + { + int invert = 1; + + if (remap_button % 2 != 0) + invert = -1; + + handle->analog_value[i][remap_button - RARCH_FIRST_CUSTOM_BIND] = 32767 * invert; + /* RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", j, k, + remap_button - RARCH_FIRST_CUSTOM_BIND, current_axis_value); */ + } } -#if 1 - /* --CURRENTLY NOT IMPLEMENTED-- - this loop should iterate on all users and all analog stick axes and if the axes are - moved and is assigned to a button it should set the bit on the mapper input bitmap. - Once implemented we should make sure to clear the original analog - stick input in input_state in input_driver.c */ for (j = 0; j < 8; j++) { @@ -180,25 +186,23 @@ void input_mapper_poll(input_mapper_t *handle) if (remap_axis < RARCH_FIRST_CUSTOM_BIND) { BIT256_SET(handle->buttons[i], remap_axis); - /* RARCH_LOG("axis %d remapped to button %d val %d\n", j, remap_axis, current_axis_value); */ + /* RARCH_LOG("axis %d remapped to button %d val %d\n", j, + remap_axis, current_axis_value); */ } else { int invert = 1; - /*if ((k == 16 && remap_axis == 17) || (k == 17 && remap_axis == 16) || - (k == 18 && remap_axis == 19) || (k == 19 && remap_axis == 18) || - (k == 20 && remap_axis == 21) || (k == 21 && remap_axis == 20) || - (k == 22 && remap_axis == 23) || (k == 23 && remap_axis == 22))*/ + if ((k % 2 == 0 && remap_axis % 2 != 0) || (k % 2 != 0 && remap_axis % 2 == 0)) invert = -1; handle->analog_value[i][remap_axis - RARCH_FIRST_CUSTOM_BIND] = current_axis_value * invert; - /* RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", j, k, remap_axis - RARCH_FIRST_CUSTOM_BIND, current_axis_value); */ + /* RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", j, k, + remap_axis - RARCH_FIRST_CUSTOM_BIND, current_axis_value); */ } } } -#endif } } } @@ -234,7 +238,7 @@ void input_mapper_state( if(handle->analog_value[port][0] || handle->analog_value[port][1]) { - *ret = val; + *ret |= val; } } if (id == 1) @@ -246,7 +250,7 @@ void input_mapper_state( if(handle->analog_value[port][2] || handle->analog_value[port][3]) { - *ret = val; + *ret |= val; } } } @@ -261,7 +265,7 @@ void input_mapper_state( if(handle->analog_value[port][4] || handle->analog_value[port][5]) { - *ret = val; + *ret |= val; } } if (id == 1) @@ -273,7 +277,7 @@ void input_mapper_state( if(handle->analog_value[port][6] || handle->analog_value[port][7]) { - *ret = val; + *ret |= val; } } } diff --git a/input/input_remapping.c b/input/input_remapping.c index 15a34c1d7d..d16d65781a 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -58,6 +58,8 @@ bool input_remapping_load_file(void *data, const char *path) char key_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; char stk_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; + bool ret = false; + char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = { "b", "y", "select", "start", "up", "down", "left", "right", @@ -85,11 +87,12 @@ bool input_remapping_load_file(void *data, const char *path) key_strings[j], '_', sizeof(btn_ident[j])); if (config_get_int(conf, btn_ident[j], &btn_remap) - && (btn_remap < RARCH_FIRST_CUSTOM_BIND && btn_remap != -1)) + && btn_remap != -1) settings->uints.input_remap_ids[i][j] = btn_remap; else if (config_get_int(conf, btn_ident[j], &btn_remap) - && (btn_remap == -1)) + && btn_remap == -1) settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; + /* else do nothing, important */ int key_remap = -1; @@ -112,11 +115,15 @@ bool input_remapping_load_file(void *data, const char *path) s3, key_strings[j]); - if (config_get_int(conf, stk_ident[j], - &stk_remap)) - settings->uints.input_remap_ids[i][j] = - stk_remap; - RARCH_LOG("stk_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]); + RARCH_LOG("pre_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]); + + if (config_get_int(conf, stk_ident[j], &stk_remap) && stk_remap != -1) + settings->uints.input_remap_ids[i][j] = stk_remap; + else if (config_get_int(conf, stk_ident[j], &stk_remap) && stk_remap == -1) + settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; + /* else do nothing, important */ + + /*RARCH_LOG("stk_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]);*/ } snprintf(s1, sizeof(s1), "input_player%u_analog_dpad_mode", i + 1); @@ -191,7 +198,7 @@ bool input_remapping_save_file(const char *path) snprintf(s2, sizeof(s2), "input_player%u_key", i + 1); snprintf(s3, sizeof(s1), "input_player%u_stk", i + 1); - for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 4; j++) + for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) { fill_pathname_join_delim(btn_ident[j], s1, key_strings[j], '_', sizeof(btn_ident[j])); @@ -201,9 +208,11 @@ bool input_remapping_save_file(const char *path) /* only save values that have been modified */ if(j < RARCH_FIRST_CUSTOM_BIND) { - if(settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] != RARCH_UNMAPPED) + if(settings->uints.input_remap_ids[i][j] != j && + settings->uints.input_remap_ids[i][j] != RARCH_UNMAPPED) config_set_int(conf, btn_ident[j], settings->uints.input_remap_ids[i][j]); - else if (settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] == RARCH_UNMAPPED) + else if (settings->uints.input_remap_ids[i][j] != j && + settings->uints.input_remap_ids[i][j] == RARCH_UNMAPPED) config_set_int(conf, btn_ident[j], -1); else config_unset(conf,btn_ident[j]); @@ -217,9 +226,14 @@ bool input_remapping_save_file(const char *path) { fill_pathname_join_delim(stk_ident[j], s3, key_strings[j], '_', sizeof(stk_ident[j])); - if(settings->uints.input_remap_ids[i][j] != j - RARCH_FIRST_CUSTOM_BIND) + if(settings->uints.input_remap_ids[i][j] != j && + settings->uints.input_remap_ids[i][j] != RARCH_UNMAPPED) config_set_int(conf, stk_ident[j], settings->uints.input_remap_ids[i][j]); + else if(settings->uints.input_remap_ids[i][j] != j && + settings->uints.input_remap_ids[i][j] == RARCH_UNMAPPED) + config_set_int(conf, stk_ident[j], + -1); else config_unset(conf,btn_ident[j]); } @@ -286,7 +300,10 @@ void input_remapping_set_defaults(bool deinit) settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; } for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) + { + RARCH_LOG("******************User: %d Val: %d\n", i, j ); settings->uints.input_remap_ids[i][j] = j; + } if (old_analog_dpad_mode[i]) settings->uints.input_analog_dpad_mode[i] = old_analog_dpad_mode[i]; From a82bb0ec94ad221bcaaa7b95ef20dd08fbee3257 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 20:21:12 +0200 Subject: [PATCH 178/517] Create special type input_bits_t --- input/connect/connect_nesusb.c | 2 +- input/connect/connect_ps2adapter.c | 6 ++++-- input/connect/connect_ps3.c | 2 +- input/connect/connect_ps4.c | 2 +- input/connect/connect_psxadapter.c | 6 ++++-- input/connect/connect_snesusb.c | 2 +- input/connect/connect_wii.c | 2 +- input/connect/connect_wiiugca.c | 2 +- input/connect/connect_wiiupro.c | 2 +- input/connect/joypad_connection.c | 3 ++- input/connect/joypad_connection.h | 4 ++-- input/drivers/cocoa_input.c | 6 +++--- input/drivers/wiiu_input.c | 2 +- input/drivers_hid/btstack_hid.c | 8 +++++--- input/drivers_hid/iohidmanager_hid.c | 5 +++-- input/drivers_hid/libusb_hid.c | 5 +++-- input/drivers_hid/null_hid.c | 3 ++- input/drivers_hid/wiiusb_hid.c | 8 +++++--- input/drivers_joypad/ctr_joypad.c | 2 +- input/drivers_joypad/gx_joypad.c | 2 +- input/drivers_joypad/hid_joypad.c | 2 +- input/drivers_joypad/linuxraw_joypad.c | 6 ++++-- input/drivers_joypad/mfi_joypad.m | 3 ++- input/drivers_joypad/null_joypad.c | 2 +- input/drivers_joypad/parport_joypad.c | 6 ++++-- input/drivers_joypad/ps3_joypad.c | 2 +- input/drivers_joypad/psp_joypad.c | 2 +- input/drivers_joypad/rwebpad_joypad.c | 7 +++---- input/drivers_joypad/switch_joypad.c | 2 +- input/drivers_joypad/udev_joypad.c | 6 ++++-- input/drivers_joypad/wiiu_joypad.c | 4 ++-- input/input_driver.c | 8 ++++---- input/input_driver.h | 16 +++++++++++----- input/input_mapper.c | 4 ++-- input/input_overlay.c | 2 +- input/input_overlay.h | 2 +- libretro-common/include/retro_miscellaneous.h | 2 -- menu/menu_event.c | 2 +- menu/menu_event.h | 2 +- retroarch.c | 10 +++++----- wiiu/input/hidpad_driver.c | 4 ++-- wiiu/input/kpad_driver.c | 4 ++-- wiiu/input/wiiu_hid.c | 3 ++- wiiu/input/wpad_driver.c | 2 +- 44 files changed, 100 insertions(+), 77 deletions(-) diff --git a/input/connect/connect_nesusb.c b/input/connect/connect_nesusb.c index 43f1f2d51b..fc484ea88d 100644 --- a/input/connect/connect_nesusb.c +++ b/input/connect/connect_nesusb.c @@ -59,7 +59,7 @@ static void hidpad_nesusb_deinit(void *data) free(device); } -static void hidpad_nesusb_get_buttons(void *data, retro_bits_t* state) +static void hidpad_nesusb_get_buttons(void *data, input_bits_t* state) { struct hidpad_nesusb_data *device = (struct hidpad_nesusb_data*)data; if (device) diff --git a/input/connect/connect_ps2adapter.c b/input/connect/connect_ps2adapter.c index c805cb85e2..4ee0e5e6cb 100644 --- a/input/connect/connect_ps2adapter.c +++ b/input/connect/connect_ps2adapter.c @@ -59,9 +59,11 @@ static void hidpad_ps2adapter_deinit(void *data) free(device); } -static void hidpad_ps2adapter_get_buttons(void *data, retro_bits_t *state) +static void hidpad_ps2adapter_get_buttons(void *data, input_bits_t *state) { - struct hidpad_ps2adapter_data *device = (struct hidpad_ps2adapter_data*)data; + struct hidpad_ps2adapter_data *device = (struct hidpad_ps2adapter_data*) + data; + if (device) { BITS_COPY16_PTR(state, device->buttons); diff --git a/input/connect/connect_ps3.c b/input/connect/connect_ps3.c index 61269a9e88..5625ee09d1 100644 --- a/input/connect/connect_ps3.c +++ b/input/connect/connect_ps3.c @@ -134,7 +134,7 @@ static void hidpad_ps3_deinit(void *data) free(device); } -static void hidpad_ps3_get_buttons(void *data, retro_bits_t *state) +static void hidpad_ps3_get_buttons(void *data, input_bits_t *state) { struct hidpad_ps3_data *device = (struct hidpad_ps3_data*)data; if ( device ) diff --git a/input/connect/connect_ps4.c b/input/connect/connect_ps4.c index 0356a91537..1de5d8d056 100644 --- a/input/connect/connect_ps4.c +++ b/input/connect/connect_ps4.c @@ -185,7 +185,7 @@ static bool hidpad_ps4_check_dpad(struct ps4 *rpt, unsigned id) return false; } -static void hidpad_ps4_get_buttons(void *data, retro_bits_t* state) +static void hidpad_ps4_get_buttons(void *data, input_bits_t* state) { struct hidpad_ps4_data *device = (struct hidpad_ps4_data*)data; struct ps4 *rpt = device ? diff --git a/input/connect/connect_psxadapter.c b/input/connect/connect_psxadapter.c index 68f921b0b6..cbc6a402f9 100644 --- a/input/connect/connect_psxadapter.c +++ b/input/connect/connect_psxadapter.c @@ -59,9 +59,11 @@ static void hidpad_psxadapter_deinit(void *data) free(device); } -static void hidpad_psxadapter_get_buttons(void *data, retro_bits_t *state) +static void hidpad_psxadapter_get_buttons(void *data, input_bits_t *state) { - struct hidpad_psxadapter_data *device = (struct hidpad_psxadapter_data*)data; + struct hidpad_psxadapter_data *device = (struct hidpad_psxadapter_data*) + data; + if (device) { BITS_COPY16_PTR(state, device->buttons); diff --git a/input/connect/connect_snesusb.c b/input/connect/connect_snesusb.c index bdc9b16a05..314577d863 100644 --- a/input/connect/connect_snesusb.c +++ b/input/connect/connect_snesusb.c @@ -60,7 +60,7 @@ static void hidpad_snesusb_deinit(void *data) free(device); } -static void hidpad_snesusb_get_buttons(void *data, retro_bits_t *state) +static void hidpad_snesusb_get_buttons(void *data, input_bits_t *state) { struct hidpad_snesusb_data *device = (struct hidpad_snesusb_data*)data; if (device) diff --git a/input/connect/connect_wii.c b/input/connect/connect_wii.c index f02e1735be..be27bfcfef 100644 --- a/input/connect/connect_wii.c +++ b/input/connect/connect_wii.c @@ -672,7 +672,7 @@ static int16_t hidpad_wii_get_axis(void *data, unsigned axis) return 0; } -static void hidpad_wii_get_buttons(void *data, retro_bits_t *state) +static void hidpad_wii_get_buttons(void *data, input_bits_t *state) { struct connect_wii_wiimote_t* device = (struct connect_wii_wiimote_t*)data; if ( device ) diff --git a/input/connect/connect_wiiugca.c b/input/connect/connect_wiiugca.c index 7e49969fd2..8283f2ac9d 100644 --- a/input/connect/connect_wiiugca.c +++ b/input/connect/connect_wiiugca.c @@ -68,7 +68,7 @@ static void hidpad_wiiugca_deinit(void *data) free(device); } -static void hidpad_wiiugca_get_buttons(void *data, retro_bits_t *state) +static void hidpad_wiiugca_get_buttons(void *data, input_bits_t *state) { struct hidpad_wiiugca_data *device = (struct hidpad_wiiugca_data*)data; if (device) diff --git a/input/connect/connect_wiiupro.c b/input/connect/connect_wiiupro.c index 836f17856e..dd9751114c 100644 --- a/input/connect/connect_wiiupro.c +++ b/input/connect/connect_wiiupro.c @@ -118,7 +118,7 @@ static void hidpad_wiiupro_deinit(void *data) free(device); } -static void hidpad_wiiupro_get_buttons(void *data, retro_bits_t *state) +static void hidpad_wiiupro_get_buttons(void *data, input_bits_t *state) { struct hidpad_wiiupro_data *device = (struct hidpad_wiiupro_data*)data; struct wiiupro *rpt = device ? diff --git a/input/connect/joypad_connection.c b/input/connect/joypad_connection.c index 1c7c75353b..777ac5874d 100644 --- a/input/connect/joypad_connection.c +++ b/input/connect/joypad_connection.c @@ -228,7 +228,8 @@ void pad_connection_packet(joypad_connection_t *joyconn, uint32_t pad, joyconn->iface->packet_handler(joyconn->data, data, length); } -void pad_connection_get_buttons(joypad_connection_t *joyconn, unsigned pad, retro_bits_t* state) +void pad_connection_get_buttons(joypad_connection_t *joyconn, + unsigned pad, input_bits_t *state) { if (joyconn && joyconn->iface) joyconn->iface->get_buttons(joyconn->data, state); diff --git a/input/connect/joypad_connection.h b/input/connect/joypad_connection.h index ec78b3c4df..67bc34bcb9 100644 --- a/input/connect/joypad_connection.h +++ b/input/connect/joypad_connection.h @@ -58,7 +58,7 @@ typedef struct pad_connection_interface void (*packet_handler)(void* device, uint8_t *packet, uint16_t size); void (*set_rumble)(void* device, enum retro_rumble_effect effect, uint16_t strength); - void (*get_buttons)(void *data, retro_bits_t *state); + void (*get_buttons)(void *data, input_bits_t *state); int16_t (*get_axis)(void *data, unsigned axis); const char* (*get_name)(void *data); } pad_connection_interface_t; @@ -90,7 +90,7 @@ void pad_connection_packet(joypad_connection_t *joyconn, uint32_t idx, uint8_t* data, uint32_t length); void pad_connection_get_buttons(joypad_connection_t *joyconn, - unsigned idx, retro_bits_t* state); + unsigned idx, input_bits_t* state); int16_t pad_connection_get_axis(joypad_connection_t *joyconn, unsigned idx, unsigned i); diff --git a/input/drivers/cocoa_input.c b/input/drivers/cocoa_input.c index da33ccef83..2cba64d44f 100644 --- a/input/drivers/cocoa_input.c +++ b/input/drivers/cocoa_input.c @@ -53,7 +53,7 @@ int32_t cocoa_input_find_any_key(void) } static int cocoa_input_find_any_button_ret(cocoa_input_data_t *apple, - retro_bits_t * state, unsigned port) + input_bits_t * state, unsigned port) { unsigned i; @@ -78,7 +78,7 @@ int32_t cocoa_input_find_any_button(uint32_t port) if (apple->joypad->get_buttons) { - retro_bits_t state; + input_bits_t state; apple->joypad->get_buttons(port,&state); ret = cocoa_input_find_any_button_ret(apple, &state, port); } @@ -93,7 +93,7 @@ int32_t cocoa_input_find_any_button(uint32_t port) if (apple->sec_joypad->get_buttons) { - retro_bits_t state; + input_bits_t state; apple->sec_joypad->poll(); apple->sec_joypad->get_buttons(port,&state); ret = cocoa_input_find_any_button_ret(apple, &state, port); diff --git a/input/drivers/wiiu_input.c b/input/drivers/wiiu_input.c index 5a188c1f21..35ae955f68 100644 --- a/input/drivers/wiiu_input.c +++ b/input/drivers/wiiu_input.c @@ -100,7 +100,7 @@ static int16_t wiiu_pointer_device_state(wiiu_input_t* wiiu, unsigned id) { case RETRO_DEVICE_ID_POINTER_PRESSED: { - retro_bits_t state; + input_bits_t state; wiiu->joypad->get_buttons(0, &state); return BIT256_GET(state, VPAD_BUTTON_TOUCH_BIT) ? 1 : 0; } diff --git a/input/drivers_hid/btstack_hid.c b/input/drivers_hid/btstack_hid.c index 1d277484aa..fb701e7505 100644 --- a/input/drivers_hid/btstack_hid.c +++ b/input/drivers_hid/btstack_hid.c @@ -1364,7 +1364,8 @@ static const char *btstack_hid_joypad_name(void *data, unsigned pad) return NULL; } -static void btstack_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state) +static void btstack_hid_joypad_get_buttons(void *data, unsigned port, + input_bits_t *state) { btstack_hid_t *hid = (btstack_hid_t*)data; if (hid) @@ -1373,9 +1374,10 @@ static void btstack_hid_joypad_get_buttons(void *data, unsigned port, retro_bits BIT256_CLEAR_ALL_PTR(state); } -static bool btstack_hid_joypad_button(void *data, unsigned port, uint16_t joykey) +static bool btstack_hid_joypad_button(void *data, + unsigned port, uint16_t joykey) { - retro_bits_t buttons; + input_bits_t buttons; btstack_hid_joypad_get_buttons(data, port, &buttons); /* Check hat. */ diff --git a/input/drivers_hid/iohidmanager_hid.c b/input/drivers_hid/iohidmanager_hid.c index b85a3c1117..8c1556c652 100644 --- a/input/drivers_hid/iohidmanager_hid.c +++ b/input/drivers_hid/iohidmanager_hid.c @@ -133,7 +133,8 @@ static const char *iohidmanager_hid_joypad_name(void *data, unsigned pad) return NULL; } -static void iohidmanager_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state) +static void iohidmanager_hid_joypad_get_buttons(void *data, + unsigned port, input_bits_t *state) { iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data; if (hid) @@ -145,7 +146,7 @@ static void iohidmanager_hid_joypad_get_buttons(void *data, unsigned port, retro static bool iohidmanager_hid_joypad_button(void *data, unsigned port, uint16_t joykey) { - retro_bits_t buttons; + input_bits_t buttons; iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data; unsigned hat_dir = GET_HAT_DIR(joykey); diff --git a/input/drivers_hid/libusb_hid.c b/input/drivers_hid/libusb_hid.c index 44580359fa..b7622b92c1 100644 --- a/input/drivers_hid/libusb_hid.c +++ b/input/drivers_hid/libusb_hid.c @@ -443,7 +443,8 @@ static const char *libusb_hid_joypad_name(void *data, unsigned pad) return NULL; } -static void libusb_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state) +static void libusb_hid_joypad_get_buttons(void *data, unsigned port, + input_bits_t *state) { libusb_hid_t *hid = (libusb_hid_t*)data; if (hid) @@ -458,7 +459,7 @@ static void libusb_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_ static bool libusb_hid_joypad_button(void *data, unsigned port, uint16_t joykey) { - retro_bits_t buttons; + input_bits_t buttons; libusb_hid_joypad_get_buttons(data, port, &buttons); /* Check hat. */ diff --git a/input/drivers_hid/null_hid.c b/input/drivers_hid/null_hid.c index 70a34d4bf8..85056ecbb1 100644 --- a/input/drivers_hid/null_hid.c +++ b/input/drivers_hid/null_hid.c @@ -38,7 +38,8 @@ static const char *null_hid_joypad_name(void *data, unsigned pad) return NULL; } -static void null_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state) +static void null_hid_joypad_get_buttons(void *data, + unsigned port, input_bits_t *state) { (void)data; (void)port; diff --git a/input/drivers_hid/wiiusb_hid.c b/input/drivers_hid/wiiusb_hid.c index 397f0a0a64..9f4fea629a 100644 --- a/input/drivers_hid/wiiusb_hid.c +++ b/input/drivers_hid/wiiusb_hid.c @@ -478,7 +478,8 @@ static bool wiiusb_hid_joypad_query(void *data, unsigned pad) return pad < MAX_USERS; } -static void wiiusb_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state) +static void wiiusb_hid_joypad_get_buttons(void *data, + unsigned port, input_bits_t *state) { wiiusb_hid_t *hid = (wiiusb_hid_t*)data; if (hid) @@ -489,9 +490,10 @@ static void wiiusb_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_ BIT256_CLEAR_ALL_PTR(state); } -static bool wiiusb_hid_joypad_button(void *data, unsigned port, uint16_t joykey) +static bool wiiusb_hid_joypad_button(void *data, + unsigned port, uint16_t joykey) { - retro_bits_t buttons; + input_bits_t buttons; wiiusb_hid_joypad_get_buttons(data, port, &buttons); diff --git a/input/drivers_joypad/ctr_joypad.c b/input/drivers_joypad/ctr_joypad.c index 711b88493d..1bfd559cd2 100644 --- a/input/drivers_joypad/ctr_joypad.c +++ b/input/drivers_joypad/ctr_joypad.c @@ -70,7 +70,7 @@ static bool ctr_joypad_button(unsigned port_num, uint16_t key) return (pad_state & (1 << key)); } -static void ctr_joypad_get_buttons(unsigned port_num, retro_bits_t *state) +static void ctr_joypad_get_buttons(unsigned port_num, input_bits_t *state) { if ( port_num < MAX_PADS ) { diff --git a/input/drivers_joypad/gx_joypad.c b/input/drivers_joypad/gx_joypad.c index 392cb602a9..1e4c0b9a4b 100644 --- a/input/drivers_joypad/gx_joypad.c +++ b/input/drivers_joypad/gx_joypad.c @@ -162,7 +162,7 @@ static bool gx_joypad_button(unsigned port, uint16_t key) return (pad_state[port] & (UINT64_C(1) << key)); } -static void gx_joypad_get_buttons(unsigned port, retro_bits_t *state) +static void gx_joypad_get_buttons(unsigned port, input_bits_t *state) { if (port < MAX_PADS) { diff --git a/input/drivers_joypad/hid_joypad.c b/input/drivers_joypad/hid_joypad.c index 203a272922..c35aff850d 100644 --- a/input/drivers_joypad/hid_joypad.c +++ b/input/drivers_joypad/hid_joypad.c @@ -55,7 +55,7 @@ static bool hid_joypad_button(unsigned port, uint16_t joykey) return false; } -static void hid_joypad_get_buttons(unsigned port, retro_bits_t *state) +static void hid_joypad_get_buttons(unsigned port, input_bits_t *state) { if (generic_hid && generic_hid->get_buttons) generic_hid->get_buttons((void*)hid_driver_get_data(), port, state); diff --git a/input/drivers_joypad/linuxraw_joypad.c b/input/drivers_joypad/linuxraw_joypad.c index 4d852f406b..b87a6e7bd0 100644 --- a/input/drivers_joypad/linuxraw_joypad.c +++ b/input/drivers_joypad/linuxraw_joypad.c @@ -326,9 +326,11 @@ static bool linuxraw_joypad_button(unsigned port, uint16_t joykey) return joykey < NUM_BUTTONS && BIT32_GET(pad->buttons, joykey); } -static void linuxraw_joypad_get_buttons(unsigned port, retro_bits_t *state) +static void linuxraw_joypad_get_buttons(unsigned port, input_bits_t *state) { - const struct linuxraw_joypad *pad = (const struct linuxraw_joypad*)&linuxraw_pads[port]; + const struct linuxraw_joypad *pad = (const struct linuxraw_joypad*) + &linuxraw_pads[port]; + if (pad) { BITS_COPY16_PTR(state, pad->buttons); diff --git a/input/drivers_joypad/mfi_joypad.m b/input/drivers_joypad/mfi_joypad.m index 94f56b2147..32f52e334c 100644 --- a/input/drivers_joypad/mfi_joypad.m +++ b/input/drivers_joypad/mfi_joypad.m @@ -221,7 +221,8 @@ static bool apple_gamecontroller_joypad_button(unsigned port, uint16_t joykey) return false; } -static void apple_gamecontroller_joypad_get_buttons(unsigned port, retro_bits_t *state) +static void apple_gamecontroller_joypad_get_buttons(unsigned port, + input_bits_t *state) { BITS_COPY16_PTR(state, mfi_buttons[port]); } diff --git a/input/drivers_joypad/null_joypad.c b/input/drivers_joypad/null_joypad.c index 058d01d5b3..d41198a8bf 100644 --- a/input/drivers_joypad/null_joypad.c +++ b/input/drivers_joypad/null_joypad.c @@ -37,7 +37,7 @@ static bool null_joypad_button(unsigned port_num, uint16_t joykey) return false; } -static void null_joypad_get_buttons(unsigned port_num, retro_bits_t *state) +static void null_joypad_get_buttons(unsigned port_num, input_bits_t *state) { BIT256_CLEAR_ALL_PTR(state); } diff --git a/input/drivers_joypad/parport_joypad.c b/input/drivers_joypad/parport_joypad.c index e236a36fc5..d392745acf 100644 --- a/input/drivers_joypad/parport_joypad.c +++ b/input/drivers_joypad/parport_joypad.c @@ -340,9 +340,11 @@ static bool parport_joypad_button(unsigned port, uint16_t joykey) return joykey < PARPORT_NUM_BUTTONS && BIT32_GET(pad->buttons, joykey); } -static void parport_joypad_get_buttons(unsigned port, retro_bits_t *state) +static void parport_joypad_get_buttons(unsigned port, input_bits_t *state) { - const struct parport_joypad *pad = (const struct parport_joypad*)&parport_pads[port]; + const struct parport_joypad *pad = (const struct parport_joypad*) + &parport_pads[port]; + if (pad) { BITS_COPY16_PTR(state, pad->buttons); diff --git a/input/drivers_joypad/ps3_joypad.c b/input/drivers_joypad/ps3_joypad.c index a85a23fcf9..0088f6d256 100644 --- a/input/drivers_joypad/ps3_joypad.c +++ b/input/drivers_joypad/ps3_joypad.c @@ -68,7 +68,7 @@ static bool ps3_joypad_button(unsigned port_num, uint16_t joykey) return pad_state[port_num] & (UINT64_C(1) << joykey); } -static void ps3_joypad_get_buttons(unsigned port_num, retro_bits_t *state) +static void ps3_joypad_get_buttons(unsigned port_num, input_bits_t *state) { if (port_num < MAX_PADS) { diff --git a/input/drivers_joypad/psp_joypad.c b/input/drivers_joypad/psp_joypad.c index d9cb4dd65a..309a8a0f3c 100644 --- a/input/drivers_joypad/psp_joypad.c +++ b/input/drivers_joypad/psp_joypad.c @@ -124,7 +124,7 @@ static bool psp_joypad_button(unsigned port_num, uint16_t key) return (pad_state[port_num] & (UINT64_C(1) << key)); } -static void psp_joypad_get_buttons(unsigned port_num, retro_bits_t *state) +static void psp_joypad_get_buttons(unsigned port_num, input_bits_t *state) { if (port_num < PSP_MAX_PADS) { diff --git a/input/drivers_joypad/rwebpad_joypad.c b/input/drivers_joypad/rwebpad_joypad.c index f061996e6e..4278afb757 100644 --- a/input/drivers_joypad/rwebpad_joypad.c +++ b/input/drivers_joypad/rwebpad_joypad.c @@ -130,12 +130,11 @@ static bool rwebpad_joypad_button(unsigned port_num, uint16_t joykey) return false; } -static void rwebpad_joypad_get_buttons(unsigned port_num, retro_bits_t *state) +static void rwebpad_joypad_get_buttons(unsigned port_num, input_bits_t *state) { EmscriptenGamepadEvent gamepad_state; - EMSCRIPTEN_RESULT r; - - r = emscripten_get_gamepad_status(port_num, &gamepad_state); + EMSCRIPTEN_RESULT r = emscripten_get_gamepad_status( + port_num, &gamepad_state); if (r == EMSCRIPTEN_RESULT_SUCCESS) { diff --git a/input/drivers_joypad/switch_joypad.c b/input/drivers_joypad/switch_joypad.c index 5a88d9c70b..eb18024ee4 100644 --- a/input/drivers_joypad/switch_joypad.c +++ b/input/drivers_joypad/switch_joypad.c @@ -60,7 +60,7 @@ static bool switch_joypad_button(unsigned port_num, uint16_t key) return (pad_state[port_num] & (1 << key)); } -static void switch_joypad_get_buttons(unsigned port_num, retro_bits_t *state) +static void switch_joypad_get_buttons(unsigned port_num, input_bits_t *state) { if(port_num < MAX_PADS) { diff --git a/input/drivers_joypad/udev_joypad.c b/input/drivers_joypad/udev_joypad.c index 9f79b19f7f..4475c62e5f 100644 --- a/input/drivers_joypad/udev_joypad.c +++ b/input/drivers_joypad/udev_joypad.c @@ -637,9 +637,11 @@ static bool udev_joypad_button(unsigned port, uint16_t joykey) return joykey < UDEV_NUM_BUTTONS && BIT64_GET(pad->buttons, joykey); } -static void udev_joypad_get_buttons(unsigned port, retro_bits_t *state) +static void udev_joypad_get_buttons(unsigned port, input_bits_t *state) { - const struct udev_joypad *pad = (const struct udev_joypad*)&udev_pads[port]; + const struct udev_joypad *pad = (const struct udev_joypad*) + &udev_pads[port]; + if (pad) { BITS_COPY16_PTR( state, pad->buttons ); diff --git a/input/drivers_joypad/wiiu_joypad.c b/input/drivers_joypad/wiiu_joypad.c index 4ae4a4863a..1f1b476439 100644 --- a/input/drivers_joypad/wiiu_joypad.c +++ b/input/drivers_joypad/wiiu_joypad.c @@ -26,7 +26,7 @@ static bool wiiu_joypad_init(void *data); static bool wiiu_joypad_query_pad(unsigned pad); static void wiiu_joypad_destroy(void); static bool wiiu_joypad_button(unsigned pad, uint16_t button); -static void wiiu_joypad_get_buttons(unsigned pad, retro_bits_t *state); +static void wiiu_joypad_get_buttons(unsigned pad, input_bits_t *state); static int16_t wiiu_joypad_axis(unsigned pad, uint32_t axis); static void wiiu_joypad_poll(void); static const char *wiiu_joypad_name(unsigned pad); @@ -110,7 +110,7 @@ static bool wiiu_joypad_button(unsigned pad, uint16_t key) return pad_drivers[pad]->button(pad, key); } -static void wiiu_joypad_get_buttons(unsigned pad, retro_bits_t *state) +static void wiiu_joypad_get_buttons(unsigned pad, input_bits_t *state) { if(!wiiu_joypad_query_pad(pad)) return; diff --git a/input/input_driver.c b/input/input_driver.c index 3df9740e19..21ab4016cf 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -823,7 +823,7 @@ void state_tracker_update_input(uint16_t *input1, uint16_t *input2) } static INLINE bool input_keys_pressed_iterate(unsigned i, - retro_bits_t* p_new_state) + input_bits_t* p_new_state) { if ((i >= RARCH_FIRST_META_KEY) && BIT64_GET(lifecycle_state, i) @@ -868,7 +868,7 @@ static INLINE bool input_keys_pressed_iterate(unsigned i, * * Returns: Input sample containing a mask of all pressed keys. */ -void input_menu_keys_pressed(void *data, retro_bits_t* p_new_state) +void input_menu_keys_pressed(void *data, input_bits_t *p_new_state) { unsigned i, port; rarch_joypad_info_t joypad_info; @@ -1073,7 +1073,7 @@ int16_t input_driver_input_state( * * Returns: Input sample containing a mask of all pressed keys. */ -void input_keys_pressed(void *data, retro_bits_t* p_new_state) +void input_keys_pressed(void *data, input_bits_t *p_new_state) { unsigned i; rarch_joypad_info_t joypad_info; @@ -1143,7 +1143,7 @@ void input_keys_pressed(void *data, retro_bits_t* p_new_state) } } -void input_get_state_for_port(void *data, unsigned port, retro_bits_t* p_new_state) +void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_state) { unsigned i, j; int16_t val; diff --git a/input/input_driver.h b/input/input_driver.h index 07ff1a285f..6ffc894387 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -38,6 +38,12 @@ RETRO_BEGIN_DECLS +typedef struct +{ + uint32_t data[8]; + uint16_t analogs[8]; +} input_bits_t; + typedef struct rarch_joypad_driver input_device_driver_t; typedef struct hid_driver hid_driver_t; @@ -172,7 +178,7 @@ struct rarch_joypad_driver bool (*query_pad)(unsigned); void (*destroy)(void); bool (*button)(unsigned, uint16_t); - void (*get_buttons)(unsigned, retro_bits_t *); + void (*get_buttons)(unsigned, input_bits_t *); int16_t (*axis)(unsigned, uint32_t); void (*poll)(void); bool (*set_rumble)(unsigned, enum retro_rumble_effect, uint16_t); @@ -187,7 +193,7 @@ struct hid_driver bool (*query_pad)(void *, unsigned); void (*free)(const void *); bool (*button)(void *, unsigned, uint16_t); - void (*get_buttons)(void *, unsigned, retro_bits_t *); + void (*get_buttons)(void *, unsigned, input_bits_t *); int16_t (*axis)(void *, unsigned, uint32_t); void (*poll)(void *); bool (*set_rumble)(void *, unsigned, enum retro_rumble_effect, uint16_t); @@ -335,15 +341,15 @@ void input_poll(void); int16_t input_state(unsigned port, unsigned device, unsigned idx, unsigned id); -void input_keys_pressed(void *data, retro_bits_t* new_state); +void input_keys_pressed(void *data, input_bits_t* new_state); #ifdef HAVE_MENU -void input_menu_keys_pressed(void *data, retro_bits_t* new_state); +void input_menu_keys_pressed(void *data, input_bits_t* new_state); #endif void *input_driver_get_data(void); -void input_get_state_for_port(void *data, unsigned port, retro_bits_t* p_new_state); +void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_state); const input_driver_t *input_get_ptr(void); diff --git a/input/input_mapper.c b/input/input_mapper.c index 67da220234..48835179a3 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -55,7 +55,7 @@ struct input_mapper /* the whole keyboard state */ uint32_t keys[RETROK_LAST / 32 + 1]; /* This is a bitmask of (1 << key_bind_id). */ - retro_bits_t buttons[MAX_USERS]; + input_bits_t buttons[MAX_USERS]; }; input_mapper_t *input_mapper_new(void) @@ -85,7 +85,7 @@ void input_mapper_poll(input_mapper_t *handle) { int i, j, k; settings_t *settings = config_get_ptr(); - retro_bits_t current_input; + input_bits_t current_input; unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); unsigned device = 0; unsigned current_button_value; diff --git a/input/input_overlay.c b/input/input_overlay.c index 99d57895bf..6e92f71989 100644 --- a/input/input_overlay.c +++ b/input/input_overlay.c @@ -46,7 +46,7 @@ typedef struct input_overlay_state int16_t analog[4]; uint32_t keys[RETROK_LAST / 32 + 1]; /* This is a bitmask of (1 << key_bind_id). */ - retro_bits_t buttons; + input_bits_t buttons; } input_overlay_state_t; struct input_overlay diff --git a/input/input_overlay.h b/input/input_overlay.h index 468e6e5141..ed70af4cd6 100644 --- a/input/input_overlay.h +++ b/input/input_overlay.h @@ -177,7 +177,7 @@ struct overlay_desc unsigned retro_key_idx; /* This is a bit mask of all input binds to set with this overlay control */ - retro_bits_t button_mask; + input_bits_t button_mask; char next_index_name[64]; diff --git a/libretro-common/include/retro_miscellaneous.h b/libretro-common/include/retro_miscellaneous.h index d39b51feca..23f9ef02ac 100644 --- a/libretro-common/include/retro_miscellaneous.h +++ b/libretro-common/include/retro_miscellaneous.h @@ -153,8 +153,6 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count) typedef struct { uint32_t data[8]; - uint16_t analogs[8]; } retro_bits_t; - #endif diff --git a/menu/menu_event.c b/menu/menu_event.c index 01eb555188..f08fa404e1 100644 --- a/menu/menu_event.c +++ b/menu/menu_event.c @@ -139,7 +139,7 @@ void menu_event_kb_set(bool down, enum retro_key key) * entire button state either but do a separate event per button * state. */ -unsigned menu_event(retro_bits_t* p_input, retro_bits_t* p_trigger_input) +unsigned menu_event(input_bits_t *p_input, input_bits_t *p_trigger_input) { menu_animation_ctx_delta_t delta; float delta_time; diff --git a/menu/menu_event.h b/menu/menu_event.h index ce00c5e5dd..cae9ec381b 100644 --- a/menu/menu_event.h +++ b/menu/menu_event.h @@ -44,7 +44,7 @@ RETRO_BEGIN_DECLS * entire button state either but do a separate event per button * state. */ -unsigned menu_event(retro_bits_t* p_input, retro_bits_t* p_trigger_state); +unsigned menu_event(input_bits_t *p_input, input_bits_t *p_trigger_state); /* Set a specific keyboard key. * diff --git a/retroarch.c b/retroarch.c index eee794302a..2bc1c37638 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2381,7 +2381,7 @@ bool runloop_msg_queue_pull(const char **ret) #ifdef HAVE_MENU static bool input_driver_toggle_button_combo( - unsigned mode, retro_bits_t* p_input) + unsigned mode, input_bits_t* p_input) { switch (mode) { @@ -2431,9 +2431,9 @@ static enum runloop_state runloop_check_state( bool input_nonblock_state, unsigned *sleep_ms) { - retro_bits_t current_input; + input_bits_t current_input; #ifdef HAVE_MENU - static retro_bits_t last_input = {{0}}; + static input_bits_t last_input = {{0}}; #endif static bool old_fs_toggle_pressed= false; static bool old_focus = true; @@ -2599,7 +2599,7 @@ static enum runloop_state runloop_check_state( #ifdef HAVE_MENU if (menu_is_alive) { - static retro_bits_t old_input = {{0}}; + static input_bits_t old_input = {{0}}; menu_ctx_iterate_t iter; retro_ctx.poll_cb(); @@ -2607,7 +2607,7 @@ static enum runloop_state runloop_check_state( { enum menu_action action; bool focused = false; - retro_bits_t trigger_input = current_input; + input_bits_t trigger_input = current_input; bits_clear_bits(trigger_input.data, old_input.data, ARRAY_SIZE(trigger_input.data)); diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c index 5cae6d710f..9bcbc8f3d0 100644 --- a/wiiu/input/hidpad_driver.c +++ b/wiiu/input/hidpad_driver.c @@ -20,7 +20,7 @@ static bool hidpad_init(void *data); static bool hidpad_query_pad(unsigned pad); static void hidpad_destroy(void); static bool hidpad_button(unsigned pad, uint16_t button); -static void hidpad_get_buttons(unsigned pad, retro_bits_t *state); +static void hidpad_get_buttons(unsigned pad, input_bits_t *state); static int16_t hidpad_axis(unsigned pad, uint32_t axis); static void hidpad_poll(void); static const char *hidpad_name(unsigned pad); @@ -116,7 +116,7 @@ static bool hidpad_button(unsigned pad, uint16_t button) #endif } -static void hidpad_get_buttons(unsigned pad, retro_bits_t *state) +static void hidpad_get_buttons(unsigned pad, input_bits_t *state) { if (!hidpad_query_pad(pad)) BIT256_CLEAR_ALL_PTR(state); diff --git a/wiiu/input/kpad_driver.c b/wiiu/input/kpad_driver.c index 48a7f428c3..de6efe7f64 100644 --- a/wiiu/input/kpad_driver.c +++ b/wiiu/input/kpad_driver.c @@ -26,7 +26,7 @@ static bool kpad_init(void *data); static bool kpad_query_pad(unsigned pad); static void kpad_destroy(void); static bool kpad_button(unsigned pad, uint16_t button); -static void kpad_get_buttons(unsigned pad, retro_bits_t *state); +static void kpad_get_buttons(unsigned pad, input_bits_t *state); static int16_t kpad_axis(unsigned pad, uint32_t axis); static void kpad_poll(void); static const char *kpad_name(unsigned pad); @@ -92,7 +92,7 @@ static bool kpad_button(unsigned pad, uint16_t button_bit) & (UINT64_C(1) << button_bit); } -static void kpad_get_buttons(unsigned pad, retro_bits_t *state) +static void kpad_get_buttons(unsigned pad, input_bits_t *state) { if (!kpad_query_pad(pad)) BIT256_CLEAR_ALL_PTR(state); diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index db0fcd074b..d0becc716e 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -38,7 +38,8 @@ static const char *wiiu_hid_joypad_name(void *data, unsigned slot) return hid->connections[slot].iface->get_name(data); } -static void wiiu_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state) +static void wiiu_hid_joypad_get_buttons(void *data, unsigned port, + input_bits_t *state) { (void)data; (void)port; diff --git a/wiiu/input/wpad_driver.c b/wiiu/input/wpad_driver.c index b707c63cb5..9dd778e19a 100644 --- a/wiiu/input/wpad_driver.c +++ b/wiiu/input/wpad_driver.c @@ -209,7 +209,7 @@ static bool wpad_button(unsigned pad, uint16_t button_bit) return button_state & (UINT64_C(1) << button_bit); } -static void wpad_get_buttons(unsigned pad, retro_bits_t *state) +static void wpad_get_buttons(unsigned pad, input_bits_t *state) { if (!wpad_query_pad(pad)) BIT256_CLEAR_ALL_PTR(state); From 460182fc63a1718d06ba84734efe44e82c864487 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 20:25:43 +0200 Subject: [PATCH 179/517] Update runahead headers --- runahead/copy_load_info.h | 6 ++++-- runahead/mem_util.h | 3 ++- runahead/mylist.h | 12 ++++++++++-- runahead/run_ahead.h | 2 ++ runahead/secondary_core.h | 4 +++- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/runahead/copy_load_info.h b/runahead/copy_load_info.h index 5671f5b753..fced73a2d6 100644 --- a/runahead/copy_load_info.h +++ b/runahead/copy_load_info.h @@ -1,15 +1,17 @@ #ifndef __COPY_LOAD_INFO_H__ #define __COPY_LOAD_INFO_H__ -#include -#include #include +#include + +#include #include "../core.h" RETRO_BEGIN_DECLS void set_load_content_info(const retro_ctx_load_content_info_t *ctx); + void set_last_core_type(enum rarch_core_type type); RETRO_END_DECLS diff --git a/runahead/mem_util.h b/runahead/mem_util.h index aedcea23d3..4ddbe21521 100644 --- a/runahead/mem_util.h +++ b/runahead/mem_util.h @@ -4,9 +4,10 @@ #include #include -#include #include +#include + #define FREE(xxxx) if ((xxxx) != NULL) { free((void*)(xxxx)); } (xxxx) = NULL RETRO_BEGIN_DECLS diff --git a/runahead/mylist.h b/runahead/mylist.h index cf35333938..1ec3e9c325 100644 --- a/runahead/mylist.h +++ b/runahead/mylist.h @@ -1,8 +1,9 @@ #ifndef __MYLIST_H__ #define __MYLIST_H__ -#include +#include #include +#include RETRO_BEGIN_DECLS @@ -19,11 +20,18 @@ typedef struct MyList_t } MyList; void *mylist_add_element(MyList *list); + void mylist_resize(MyList *list, int newSize, bool runConstructor); -void mylist_create(MyList **list_p, int initialCapacity, constructor_t constructor, destructor_t destructor); + +void mylist_create(MyList **list_p, int initialCapacity, + constructor_t constructor, destructor_t destructor); + void mylist_destroy(MyList **list_p); + void mylist_assign(MyList *list, int index, void *value); + void mylist_remove_at(MyList *list, int index); + void mylist_pop_front(MyList *list); RETRO_END_DECLS diff --git a/runahead/run_ahead.h b/runahead/run_ahead.h index f1508dd657..8868ce345b 100644 --- a/runahead/run_ahead.h +++ b/runahead/run_ahead.h @@ -1,7 +1,9 @@ #ifndef __RUN_AHEAD_H__ #define __RUN_AHEAD_H__ +#include #include + #include RETRO_BEGIN_DECLS diff --git a/runahead/secondary_core.h b/runahead/secondary_core.h index 324c882c6d..9eb4190d09 100644 --- a/runahead/secondary_core.h +++ b/runahead/secondary_core.h @@ -1,9 +1,11 @@ #ifndef __SECONDARY_CORE_H__ #define __SECONDARY_CORE_H__ -#include +#include #include +#include + #include "../core_type.h" RETRO_BEGIN_DECLS From 1ce19f95731561fe4ab5ad6fd8336a4ecb1eded5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 20:26:47 +0200 Subject: [PATCH 180/517] Include header --- runahead/copy_load_info.c | 1 + runahead/dirty_input.c | 1 + 2 files changed, 2 insertions(+) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c index e78c6238ad..09044efda2 100644 --- a/runahead/copy_load_info.c +++ b/runahead/copy_load_info.c @@ -6,6 +6,7 @@ #include "../core.h" #include "mem_util.h" +#include "copy_load_info.h" retro_ctx_load_content_info_t *load_content_info; enum rarch_core_type last_core_type; diff --git a/runahead/dirty_input.c b/runahead/dirty_input.c index 5330200de2..d1b0686ed6 100644 --- a/runahead/dirty_input.c +++ b/runahead/dirty_input.c @@ -7,6 +7,7 @@ #include "mylist.h" #include "mem_util.h" +#include "dirty_input.h" bool input_is_dirty = false; static MyList *input_state_list = NULL; From 368becbc4258e2c3453f8bc59f2424b90022f7cd Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 20:29:07 +0200 Subject: [PATCH 181/517] (wiiu) Potential build fix --- Makefile.wiiu | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Makefile.wiiu b/Makefile.wiiu index bcba3cd881..b3f57e8918 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -91,14 +91,6 @@ else ifeq ($(HAVE_RUNAHEAD),1) DEFINES += -DHAVE_RUNAHEAD -ifneq ($(GRIFFIN_BUILD), 1) - OBJ += runahead/copy_load_info.o - OBJ += runahead/dirty_input.o - OBJ += runahead/mem_util.o - OBJ += runahead/mylist.o - OBJ += runahead/run_ahead.o - OBJ += runahead/secondary_core.o -endif endif OBJ += wiiu/system/missing_libc_functions.o From 0ed69fd170ddaad7981e279b33159b3acd7022a0 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 20:49:02 +0200 Subject: [PATCH 182/517] Simplify input_state analog code remapping somewhat --- input/input_driver.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 21ab4016cf..ef4d32d69f 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -659,33 +659,15 @@ int16_t input_state(unsigned port, unsigned device, case RETRO_DEVICE_JOYPAD: if (id != settings->uints.input_remap_ids[port][id]) reset_state = true; - break; case RETRO_DEVICE_ANALOG: if (idx < 2 && id < 2) { - if (idx == 0) - { - if (id == 0 && settings->uints.input_remap_ids[port][16] != 16) - reset_state = true; - if (id == 0 && settings->uints.input_remap_ids[port][17] != 17) - reset_state = true; - if (id == 1 && settings->uints.input_remap_ids[port][18] != 18) - reset_state = true; - if (id == 1 && settings->uints.input_remap_ids[port][19] != 19) - reset_state = true; - } - if (idx == 1) - { - if (id == 0 && settings->uints.input_remap_ids[port][20] != 20) - reset_state = true; - if (id == 0 && settings->uints.input_remap_ids[port][21] != 21) - reset_state = true; - if (id == 1 && settings->uints.input_remap_ids[port][22] != 22) - reset_state = true; - if (id == 1 && settings->uints.input_remap_ids[port][23] != 23) - reset_state = true; - } + unsigned offset = RARCH_FIRST_CUSTOM_BIND + (idx * 4) + (id * 2); + if (settings->uints.input_remap_ids[port][offset] != offset) + reset_state = true; + if (settings->uints.input_remap_ids[port][offset+1] != (offset+1)) + reset_state = true; } break; } From 30090db941f3a71ccd448e3372d59a7ab434512a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 21:04:42 +0200 Subject: [PATCH 183/517] Simplify input_get_state_for_port for analogs --- input/input_driver.c | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index ef4d32d69f..ffff8e397e 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1128,7 +1128,6 @@ void input_keys_pressed(void *data, input_bits_t *p_new_state) void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_state) { unsigned i, j; - int16_t val; rarch_joypad_info_t joypad_info; settings_t *settings = (settings_t*)data; BIT256_CLEAR_ALL_PTR(p_new_state); @@ -1148,34 +1147,20 @@ void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_sta BIT256_SET_PTR(p_new_state, i); } - /* left-stick x */ - val = input_joypad_analog(input_driver_get_joypad_driver(), joypad_info, port, 0, 0, libretro_input_binds[port]); - if (val >= 0) - p_new_state->analogs[0] = val; - else - p_new_state->analogs[1] = val; - - /* left-stick y */ - val = input_joypad_analog(input_driver_get_joypad_driver(), joypad_info, port, 0, 1, libretro_input_binds[port]); - if (val >= 0) - p_new_state->analogs[2] = val; - else - p_new_state->analogs[3] = val; - - /* right-stick x */ - val = input_joypad_analog(input_driver_get_joypad_driver(), joypad_info, port, 1, 0, libretro_input_binds[port]); - if (val >= 0) - p_new_state->analogs[4] = val; - else - p_new_state->analogs[5] = val; - - /* right-stick y */ - val = input_joypad_analog(input_driver_get_joypad_driver(), joypad_info, port, 1, 1, libretro_input_binds[port]); - if (val >= 0) - p_new_state->analogs[6] = val; - else - p_new_state->analogs[7] = val; + for (i = 0; i < 2; i++) + { + for (j = 0; j < 2; j++) + { + unsigned offset = 0 + (i * 4) + (j * 2); + int16_t val = input_joypad_analog(input_driver_get_joypad_driver(), + joypad_info, port, i, j, libretro_input_binds[port]); + if (val >= 0) + p_new_state->analogs[offset] = val; + else + p_new_state->analogs[offset+1] = val; + } + } } void *input_driver_get_data(void) From 24b694d6742944498a33533731398bed2dd79e05 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 21:07:04 +0200 Subject: [PATCH 184/517] Grab joypad_driver only once --- input/input_driver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/input/input_driver.c b/input/input_driver.c index ffff8e397e..b4092403b4 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1130,6 +1130,8 @@ void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_sta unsigned i, j; rarch_joypad_info_t joypad_info; settings_t *settings = (settings_t*)data; + const input_device_driver_t *joypad_driver = input_driver_get_joypad_driver(); + BIT256_CLEAR_ALL_PTR(p_new_state); joypad_info.joy_idx = settings->uints.input_joypad_map[port]; @@ -1152,7 +1154,7 @@ void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_sta for (j = 0; j < 2; j++) { unsigned offset = 0 + (i * 4) + (j * 2); - int16_t val = input_joypad_analog(input_driver_get_joypad_driver(), + int16_t val = input_joypad_analog(joypad_driver, joypad_info, port, i, j, libretro_input_binds[port]); if (val >= 0) From 9bcaac1abcb85657e16d65f6e660b1e246e73c13 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 21:38:57 +0200 Subject: [PATCH 185/517] Simplify input_mapper_state --- input/input_mapper.c | 122 +++++++++++++------------------------------ 1 file changed, 37 insertions(+), 85 deletions(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index 48835179a3..e180f7589b 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -83,21 +83,16 @@ bool input_mapper_button_pressed(input_mapper_t *handle, unsigned port, unsigned void input_mapper_poll(input_mapper_t *handle) { - int i, j, k; - settings_t *settings = config_get_ptr(); + int i, j; input_bits_t current_input; + settings_t *settings = config_get_ptr(); unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); unsigned device = 0; - unsigned current_button_value; int16_t current_axis_value; - unsigned remap_button, remap_axis; bool key_event[RARCH_CUSTOM_BIND_LIST_END]; -#ifdef HAVE_MENU - bool menu_is_alive = menu_driver_is_alive(); -#endif #ifdef HAVE_MENU - if (menu_is_alive) + if (menu_driver_is_alive()) return; #endif @@ -105,7 +100,7 @@ void input_mapper_poll(input_mapper_t *handle) for (i = 0; i < max_users; i++) { - device = settings->uints.input_libretro_device[i]; + device = settings->uints.input_libretro_device[i]; device &= RETRO_DEVICE_MASK; /* keyboard to gamepad remapping */ @@ -114,28 +109,27 @@ void input_mapper_poll(input_mapper_t *handle) input_get_state_for_port(settings, i, ¤t_input); for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) { - { - current_button_value = BIT256_GET(current_input, j); - remap_button = settings->uints.input_keymapper_ids[i][j]; - if (current_button_value == 1 && j != remap_button && + unsigned current_button_value = BIT256_GET(current_input, j); + unsigned remap_button = settings->uints.input_keymapper_ids[i][j]; + + if (current_button_value == 1 && j != remap_button && remap_button != RETROK_UNKNOWN) - { - MAPPER_SET_KEY (handle, + { + MAPPER_SET_KEY (handle, remap_button); - input_keyboard_event(true, + input_keyboard_event(true, + remap_button, + 0, 0, RETRO_DEVICE_KEYBOARD); + key_event[j] = true; + } + else + { + if (key_event[j] == false && + remap_button != RETROK_UNKNOWN) + { + input_keyboard_event(false, remap_button, 0, 0, RETRO_DEVICE_KEYBOARD); - key_event[j] = true; - } - else - { - if (key_event[j] == false && - remap_button != RETROK_UNKNOWN) - { - input_keyboard_event(false, - remap_button, - 0, 0, RETRO_DEVICE_KEYBOARD); - } } } } @@ -155,8 +149,9 @@ void input_mapper_poll(input_mapper_t *handle) for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) { - current_button_value = BIT256_GET(current_input, j); - remap_button = settings->uints.input_remap_ids[i][j]; + unsigned current_button_value = BIT256_GET(current_input, j); + unsigned remap_button = settings->uints.input_remap_ids[i][j]; + if (current_button_value == 1 && j != remap_button && remap_button != RARCH_UNMAPPED && remap_button < RARCH_FIRST_CUSTOM_BIND) BIT256_SET(handle->buttons[i], remap_button); @@ -176,10 +171,10 @@ void input_mapper_poll(input_mapper_t *handle) for (j = 0; j < 8; j++) { - k = j + RARCH_FIRST_CUSTOM_BIND; - + unsigned remap_axis; + unsigned k = j + RARCH_FIRST_CUSTOM_BIND; current_axis_value = current_input.analogs[j]; - remap_axis = settings->uints.input_remap_ids[i][k]; + remap_axis = settings->uints.input_remap_ids[i][k]; if (current_axis_value != 0 && k != remap_axis && remap_axis != RARCH_UNMAPPED) { @@ -225,62 +220,19 @@ void input_mapper_state( *ret = 1; break; case RETRO_DEVICE_ANALOG: + if (idx < 2 && id < 2) { - int val = 0; - if (idx == 0) - { - if (id == 0) - { - if (handle->analog_value[port][0]) - val = handle->analog_value[port][0]; - else if (handle->analog_value[port][1]) - val = handle->analog_value[port][1]; + int val = 0; + unsigned offset = 0 + (idx * 4) + (id * 2); - if(handle->analog_value[port][0] || handle->analog_value[port][1]) - { - *ret |= val; - } - } - if (id == 1) - { - if (handle->analog_value[port][2]) - val = handle->analog_value[port][2]; - else if (handle->analog_value[port][3]) - val = handle->analog_value[port][3]; + if (handle->analog_value[port][offset]) + val = handle->analog_value[port][offset]; + else if (handle->analog_value[port][offset+1]) + val = handle->analog_value[port][offset+1]; - if(handle->analog_value[port][2] || handle->analog_value[port][3]) - { - *ret |= val; - } - } - } - if (idx == 1) - { - if (id == 0) - { - if (handle->analog_value[port][4]) - val = handle->analog_value[port][4]; - else if (handle->analog_value[port][5]) - val = handle->analog_value[port][5]; - - if(handle->analog_value[port][4] || handle->analog_value[port][5]) - { - *ret |= val; - } - } - if (id == 1) - { - if (handle->analog_value[port][6]) - val = handle->analog_value[port][6]; - else if (handle->analog_value[port][7]) - val = handle->analog_value[port][7]; - - if(handle->analog_value[port][6] || handle->analog_value[port][7]) - { - *ret |= val; - } - } - } + if ( handle->analog_value[port][offset] || + handle->analog_value[port][offset+1]) + *ret |= val; } break; case RETRO_DEVICE_KEYBOARD: From 1dd73f86b7be5835a02a81a753fc2e683e0dda09 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 21:45:10 +0200 Subject: [PATCH 186/517] Simplify this code somewhat --- input/input_driver.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index b4092403b4..98845510fc 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -972,7 +972,10 @@ void input_menu_keys_pressed(void *data, input_bits_t *p_new_state) } } - if (bit_pressed || input_keys_pressed_iterate(i, p_new_state)) + if (!bit_pressed) + bit_pressed = input_keys_pressed_iterate(i, p_new_state); + + if (bit_pressed) { BIT256_SET_PTR(p_new_state, i); } @@ -1117,8 +1120,10 @@ void input_keys_pressed(void *data, input_bits_t *p_new_state) 0, RETRO_DEVICE_JOYPAD, 0, i) ) bit_pressed = true; + else if (input_keys_pressed_iterate(i, p_new_state)) + bit_pressed = true; - if (bit_pressed || input_keys_pressed_iterate(i, p_new_state)) + if (bit_pressed) { BIT256_SET_PTR(p_new_state, i); } From 968c692a0469e60b8b8ac1466edcb6fcc8afb7b4 Mon Sep 17 00:00:00 2001 From: radius Date: Sun, 8 Apr 2018 14:48:11 -0500 Subject: [PATCH 187/517] remap-redux: allow the menu to display manual keybinds --- input/input_driver.c | 2 +- input/input_remapping.c | 2 +- menu/menu_displaylist.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 98845510fc..8cd95fe416 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -2576,7 +2576,7 @@ static void input_config_get_bind_string_joykey( { if (bind->joykey_label && !string_is_empty(bind->joykey_label) && label_show) - snprintf(buf, size, "%s %s ", prefix, bind->joykey_label); + snprintf(buf, size, "%s %s (hat)", prefix, bind->joykey_label); else { const char *dir = "?"; diff --git a/input/input_remapping.c b/input/input_remapping.c index d16d65781a..ed71689ab2 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -115,7 +115,7 @@ bool input_remapping_load_file(void *data, const char *path) s3, key_strings[j]); - RARCH_LOG("pre_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]); + /* RARCH_LOG("pre_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]); */ if (config_get_int(conf, stk_ident[j], &stk_remap) && stk_remap != -1) settings->uints.input_remap_ids[i][j] = stk_remap; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index d8a276e72a..77d616a855 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3271,7 +3271,8 @@ static int menu_displaylist_parse_options_remappings( const struct retro_keybind *keyptr = &input_config_binds[p][retro_id]; - strlcpy(descriptor, msg_hash_to_str(keyptr->enum_idx), sizeof(descriptor)); + snprintf(desc_label, sizeof(desc_label), "%s %s", msg_hash_to_str(keyptr->enum_idx), descriptor); + strlcpy(descriptor, desc_label, sizeof(descriptor)); } menu_entries_append_enum(info->list, descriptor, "", From a646fd3e65c30f7840e67b3f9cd9f16110f0d221 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 22:36:48 +0200 Subject: [PATCH 188/517] (input_mapper.c) Small non-functional cleanups --- input/input_mapper.c | 208 ++++++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 90 deletions(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index e180f7589b..0b2941d9a3 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -58,6 +58,11 @@ struct input_mapper input_bits_t buttons[MAX_USERS]; }; +static bool input_mapper_button_pressed(input_mapper_t *handle, unsigned port, unsigned id) +{ + return BIT256_GET(handle->buttons[port], id); +} + input_mapper_t *input_mapper_new(void) { input_mapper_t* handle = (input_mapper_t*) @@ -76,19 +81,12 @@ void input_mapper_free(input_mapper_t *handle) free (handle); } -bool input_mapper_button_pressed(input_mapper_t *handle, unsigned port, unsigned id) -{ - return BIT256_GET(handle->buttons[port], id); -} - void input_mapper_poll(input_mapper_t *handle) { int i, j; input_bits_t current_input; settings_t *settings = config_get_ptr(); unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); - unsigned device = 0; - int16_t current_axis_value; bool key_event[RARCH_CUSTOM_BIND_LIST_END]; #ifdef HAVE_MENU @@ -100,104 +98,135 @@ void input_mapper_poll(input_mapper_t *handle) for (i = 0; i < max_users; i++) { - device = settings->uints.input_libretro_device[i]; - device &= RETRO_DEVICE_MASK; + unsigned device = settings->uints.input_libretro_device[i]; + device &= RETRO_DEVICE_MASK; - /* keyboard to gamepad remapping */ - if (device == RETRO_DEVICE_KEYBOARD) + switch (device) { - input_get_state_for_port(settings, i, ¤t_input); - for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) - { - unsigned current_button_value = BIT256_GET(current_input, j); - unsigned remap_button = settings->uints.input_keymapper_ids[i][j]; + /* keyboard to gamepad remapping */ + case RETRO_DEVICE_KEYBOARD: + input_get_state_for_port(settings, i, ¤t_input); + for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) + { + unsigned current_button_value = BIT256_GET(current_input, j); + unsigned remap_button = + settings->uints.input_keymapper_ids[i][j]; - if (current_button_value == 1 && j != remap_button && - remap_button != RETROK_UNKNOWN) - { - MAPPER_SET_KEY (handle, - remap_button); - input_keyboard_event(true, - remap_button, - 0, 0, RETRO_DEVICE_KEYBOARD); - key_event[j] = true; - } - else - { - if (key_event[j] == false && - remap_button != RETROK_UNKNOWN) + if ( + (current_button_value == 1) && + (j != remap_button) && + (remap_button != RETROK_UNKNOWN) + ) { - input_keyboard_event(false, + MAPPER_SET_KEY (handle, + remap_button); + input_keyboard_event(true, remap_button, 0, 0, RETRO_DEVICE_KEYBOARD); + key_event[j] = true; + } + else + { + if ( (key_event[j] == false) && + (remap_button != RETROK_UNKNOWN) + ) + { + input_keyboard_event(false, + remap_button, + 0, 0, RETRO_DEVICE_KEYBOARD); + } } } - } - } + break; - /* gamepad remapping */ - if (device == RETRO_DEVICE_JOYPAD || device == RETRO_DEVICE_ANALOG) - { - /* this loop iterates on all users and all buttons, and checks if a pressed button - is assigned to any other button than the default one, then it sets the bit on the - mapper input bitmap, later on the original input is cleared in input_state */ - BIT256_CLEAR_ALL(handle->buttons[i]); - for (j = 0; j < 8; j++) - handle->analog_value[i][j] = 0; + /* gamepad remapping */ + case RETRO_DEVICE_JOYPAD: + case RETRO_DEVICE_ANALOG: + /* this loop iterates on all users and all buttons, + * and checks if a pressed button is assigned to any + * other button than the default one, then it sets + * the bit on the mapper input bitmap, later on the + * original input is cleared in input_state */ + BIT256_CLEAR_ALL(handle->buttons[i]); - input_get_state_for_port(settings, i, ¤t_input); + for (j = 0; j < 8; j++) + handle->analog_value[i][j] = 0; - for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) - { - unsigned current_button_value = BIT256_GET(current_input, j); - unsigned remap_button = settings->uints.input_remap_ids[i][j]; + input_get_state_for_port(settings, i, ¤t_input); - if (current_button_value == 1 && j != remap_button && - remap_button != RARCH_UNMAPPED && remap_button < RARCH_FIRST_CUSTOM_BIND) - BIT256_SET(handle->buttons[i], remap_button); - else if (current_button_value == 1 && j != remap_button && - remap_button != RARCH_UNMAPPED && remap_button >= RARCH_FIRST_CUSTOM_BIND) + for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) { + unsigned current_button_value = BIT256_GET(current_input, j); + unsigned remap_button = + settings->uints.input_remap_ids[i][j]; + + if ( + (current_button_value == 1) && + (j != remap_button) && + (remap_button != RARCH_UNMAPPED) && + (remap_button < RARCH_FIRST_CUSTOM_BIND) + ) + BIT256_SET(handle->buttons[i], remap_button); + else if ( + (current_button_value == 1) && + (j != remap_button) && + (remap_button != RARCH_UNMAPPED) && + (remap_button >= RARCH_FIRST_CUSTOM_BIND) + ) + { int invert = 1; if (remap_button % 2 != 0) invert = -1; - handle->analog_value[i][remap_button - RARCH_FIRST_CUSTOM_BIND] = 32767 * invert; - /* RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", j, k, - remap_button - RARCH_FIRST_CUSTOM_BIND, current_axis_value); */ + handle->analog_value[i][ + remap_button - RARCH_FIRST_CUSTOM_BIND] = + 32767 * invert; + } } - } - for (j = 0; j < 8; j++) - { - unsigned remap_axis; - unsigned k = j + RARCH_FIRST_CUSTOM_BIND; - current_axis_value = current_input.analogs[j]; - remap_axis = settings->uints.input_remap_ids[i][k]; - - if (current_axis_value != 0 && k != remap_axis && remap_axis != RARCH_UNMAPPED) + for (j = 0; j < 8; j++) { - if (remap_axis < RARCH_FIRST_CUSTOM_BIND) - { - BIT256_SET(handle->buttons[i], remap_axis); - /* RARCH_LOG("axis %d remapped to button %d val %d\n", j, - remap_axis, current_axis_value); */ - } - else - { - int invert = 1; + unsigned k = j + RARCH_FIRST_CUSTOM_BIND; + int16_t current_axis_value = current_input.analogs[j]; + unsigned remap_axis = + settings->uints.input_remap_ids[i][k]; - if ((k % 2 == 0 && remap_axis % 2 != 0) || (k % 2 != 0 && remap_axis % 2 == 0)) - invert = -1; + if ( + (current_axis_value != 0) && + (k != remap_axis) && + (remap_axis != RARCH_UNMAPPED) + ) + { + if (remap_axis < RARCH_FIRST_CUSTOM_BIND) + { + BIT256_SET(handle->buttons[i], remap_axis); + } + else + { + int invert = 1; - handle->analog_value[i][remap_axis - RARCH_FIRST_CUSTOM_BIND] = current_axis_value * invert; - /* RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", j, k, - remap_axis - RARCH_FIRST_CUSTOM_BIND, current_axis_value); */ + if ( (k % 2 == 0 && remap_axis % 2 != 0) || + (k % 2 != 0 && remap_axis % 2 == 0) + ) + invert = -1; + + handle->analog_value[i][ + remap_axis - RARCH_FIRST_CUSTOM_BIND] = + current_axis_value * invert; +#if 0 + RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", + j, k, + remap_axis - RARCH_FIRST_CUSTOM_BIND, + current_axis_value); +#endif + } } + } - - } + break; + default: + break; } } } @@ -217,30 +246,29 @@ void input_mapper_state( { case RETRO_DEVICE_JOYPAD: if (input_mapper_button_pressed(handle, port, id)) - *ret = 1; + *ret = 1; break; case RETRO_DEVICE_ANALOG: if (idx < 2 && id < 2) { int val = 0; unsigned offset = 0 + (idx * 4) + (id * 2); + int val1 = handle->analog_value[port][offset]; + int val2 = handle->analog_value[port][offset+1]; - if (handle->analog_value[port][offset]) - val = handle->analog_value[port][offset]; - else if (handle->analog_value[port][offset+1]) - val = handle->analog_value[port][offset+1]; + if (val1) + val = val1; + else if (val2) + val = val2; - if ( handle->analog_value[port][offset] || - handle->analog_value[port][offset+1]) - *ret |= val; + if (val1 || val2) + *ret |= val; } break; case RETRO_DEVICE_KEYBOARD: if (id < RETROK_LAST) - { if (MAPPER_GET_KEY(handle, id)) *ret |= 1; - } break; default: break; From 4052ffe08e4caa9d514753d37a3d78c0d0b66b9e Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Sun, 8 Apr 2018 22:40:02 +0200 Subject: [PATCH 189/517] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 400723e915..fa9879b423 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ # 1.7.2 (future) - ANDROID/OPENSL: Prevent crashes when setting audio latency too low (buffer count can never be lower than 2 now). +- COMMON: Hide the 'Core delete' option if the 'Core updater' is also hidden. - COMMON: Add way to reset core association for playlist entry. - COMMON: Fix invalid long command line options causing infinite loop on Windows - COMMON: Add OSD statistics for video/audio/core. From bc704770058809313e398304c7ead5328c845f64 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 23:08:21 +0200 Subject: [PATCH 190/517] (input_mapper.c) Some more refactors --- input/input_mapper.c | 67 ++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index 0b2941d9a3..dd0e1ad3c5 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -108,28 +108,24 @@ void input_mapper_poll(input_mapper_t *handle) input_get_state_for_port(settings, i, ¤t_input); for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) { - unsigned current_button_value = BIT256_GET(current_input, j); unsigned remap_button = settings->uints.input_keymapper_ids[i][j]; + bool remap_valid = remap_button != RETROK_UNKNOWN; - if ( - (current_button_value == 1) && - (j != remap_button) && - (remap_button != RETROK_UNKNOWN) - ) + if (remap_valid) { - MAPPER_SET_KEY (handle, - remap_button); - input_keyboard_event(true, - remap_button, - 0, 0, RETRO_DEVICE_KEYBOARD); - key_event[j] = true; - } - else - { - if ( (key_event[j] == false) && - (remap_button != RETROK_UNKNOWN) - ) + unsigned current_button_value = BIT256_GET(current_input, j); + + if ((current_button_value == 1) && (j != remap_button)) + { + MAPPER_SET_KEY (handle, + remap_button); + input_keyboard_event(true, + remap_button, + 0, 0, RETRO_DEVICE_KEYBOARD); + key_event[j] = true; + } + else if (!key_event[j]) { input_keyboard_event(false, remap_button, @@ -159,29 +155,26 @@ void input_mapper_poll(input_mapper_t *handle) unsigned current_button_value = BIT256_GET(current_input, j); unsigned remap_button = settings->uints.input_remap_ids[i][j]; + bool remap_valid = (current_button_value == 1) && + (j != remap_button) && (remap_button != RARCH_UNMAPPED); - if ( - (current_button_value == 1) && - (j != remap_button) && - (remap_button != RARCH_UNMAPPED) && - (remap_button < RARCH_FIRST_CUSTOM_BIND) - ) - BIT256_SET(handle->buttons[i], remap_button); - else if ( - (current_button_value == 1) && - (j != remap_button) && - (remap_button != RARCH_UNMAPPED) && - (remap_button >= RARCH_FIRST_CUSTOM_BIND) - ) + if (remap_valid) { - int invert = 1; + if (remap_button < RARCH_FIRST_CUSTOM_BIND) + { + BIT256_SET(handle->buttons[i], remap_button); + } + else if (remap_button >= RARCH_FIRST_CUSTOM_BIND) + { + int invert = 1; - if (remap_button % 2 != 0) - invert = -1; + if (remap_button % 2 != 0) + invert = -1; - handle->analog_value[i][ - remap_button - RARCH_FIRST_CUSTOM_BIND] = - 32767 * invert; + handle->analog_value[i][ + remap_button - RARCH_FIRST_CUSTOM_BIND] = + 32767 * invert; + } } } From 3a4d0942774dd36d706dffb56be3184dcf2587d1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 23:13:20 +0200 Subject: [PATCH 191/517] MSVC buildfix --- input/input_mapper.c | 2 +- input/input_remapping.c | 3 +-- menu/cbs/menu_cbs_left.c | 3 +-- menu/cbs/menu_cbs_sublabel.c | 8 +++----- runahead/dirty_input.c | 4 ++-- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index dd0e1ad3c5..1ccb42a975 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -83,7 +83,7 @@ void input_mapper_free(input_mapper_t *handle) void input_mapper_poll(input_mapper_t *handle) { - int i, j; + unsigned i, j; input_bits_t current_input; settings_t *settings = config_get_ptr(); unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); diff --git a/input/input_remapping.c b/input/input_remapping.c index ed71689ab2..b8a0b9ed18 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -80,6 +80,7 @@ bool input_remapping_load_file(void *data, const char *path) for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) { int btn_remap = -1; + int key_remap = -1; fill_pathname_join_delim(btn_ident[j], s1, key_strings[j], '_', sizeof(btn_ident[j])); @@ -94,8 +95,6 @@ bool input_remapping_load_file(void *data, const char *path) settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; /* else do nothing, important */ - int key_remap = -1; - if (config_get_int(conf, key_ident[j], &key_remap)) settings->uints.input_keymapper_ids[i][j] = key_remap; else diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 53115b011d..902c393fe6 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -127,9 +127,8 @@ static int action_left_input_desc(unsigned type, const char *label, static int action_left_input_desc_kbd(unsigned type, const char *label, bool wraparound) { - char desc[PATH_MAX_LENGTH]; - unsigned key_id, id, offset; unsigned remap_id; + unsigned key_id, id, offset; settings_t *settings = config_get_ptr(); if (!settings) diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 735b2911b7..d5b83c6c8c 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -413,9 +413,9 @@ static int action_bind_sublabel_subsystem_add( const char *label, const char *path, char *s, size_t len) { - rarch_system_info_t *system = runloop_get_system_info(); - const struct retro_subsystem_info* subsystem = NULL; - subsystem = system->subsystem.data + (type - MENU_SETTINGS_SUBSYSTEM_ADD); + rarch_system_info_t *system = runloop_get_system_info(); + const struct retro_subsystem_info *subsystem = system ? + system->subsystem.data + (type - MENU_SETTINGS_SUBSYSTEM_ADD) : NULL; if (subsystem && content_get_subsystem_rom_id() < subsystem->num_roms) snprintf(s, len, " Current Content: %s", @@ -432,7 +432,6 @@ static int action_bind_sublabel_remap_kbd_sublabel( const char *label, const char *path, char *s, size_t len) { - char desc[PATH_MAX_LENGTH]; unsigned offset; settings_t *settings = config_get_ptr(); @@ -458,7 +457,6 @@ static int action_bind_sublabel_remap_sublabel( const char *label, const char *path, char *s, size_t len) { - char desc[PATH_MAX_LENGTH]; unsigned offset; settings_t *settings = config_get_ptr(); diff --git a/runahead/dirty_input.c b/runahead/dirty_input.c index d1b0686ed6..5c2086a53a 100644 --- a/runahead/dirty_input.c +++ b/runahead/dirty_input.c @@ -56,7 +56,7 @@ static void input_state_set_last(unsigned port, unsigned device, InputListElementConstructor, free); /* find list item */ - for (i = 0; i < input_state_list->size; i++) + for (i = 0; i < (unsigned)input_state_list->size; i++) { element = (InputListElement*)input_state_list->data[i]; if ( element->port == port @@ -84,7 +84,7 @@ static int16_t input_state_get_last(unsigned port, return 0; /* find list item */ - for (i = 0; i < input_state_list->size; i++) + for (i = 0; i < (unsigned)input_state_list->size; i++) { InputListElement *element = (InputListElement*)input_state_list->data[i]; From 8dc63653e5465318bcd5086fdef11bc69bbc9a61 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Sun, 8 Apr 2018 23:17:45 +0200 Subject: [PATCH 192/517] Remove unused variables --- input/input_overlay.c | 1 - input/input_remapping.c | 2 -- menu/menu_displaylist.c | 4 ---- 3 files changed, 7 deletions(-) diff --git a/input/input_overlay.c b/input/input_overlay.c index 6e92f71989..e0b5915869 100644 --- a/input/input_overlay.c +++ b/input/input_overlay.c @@ -113,7 +113,6 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc, else { /*we need ALL of the inputs to be active*/ - all_buttons_pressed = false; desc->updated = false; /*abort*/ diff --git a/input/input_remapping.c b/input/input_remapping.c index b8a0b9ed18..ee1d13fd2f 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -58,8 +58,6 @@ bool input_remapping_load_file(void *data, const char *path) char key_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; char stk_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - bool ret = false; - char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = { "b", "y", "select", "start", "up", "down", "left", "right", diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index eac2efb79e..c787a83c10 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3255,8 +3255,6 @@ static int menu_displaylist_parse_options_remappings( for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND + 8; retro_id++) { char desc_label[64]; - unsigned user = p + 1; - unsigned desc_offset = retro_id; char descriptor[255]; const struct retro_keybind *auto_bind = NULL; const struct retro_keybind *keybind = NULL; @@ -3299,8 +3297,6 @@ static int menu_displaylist_parse_options_remappings( { for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) { - unsigned user = settings->uints.keymapper_port + 1; - unsigned desc_offset = retro_id; char descriptor[255]; const struct retro_keybind *auto_bind = NULL; const struct retro_keybind *keybind = NULL; From 4cd4de3bd3a3290fb059ef01952e630747e87546 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 23:20:06 +0200 Subject: [PATCH 193/517] Simplify input_overlay_add_inputs_inner --- input/input_overlay.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/input/input_overlay.c b/input/input_overlay.c index e0b5915869..858457cf76 100644 --- a/input/input_overlay.c +++ b/input/input_overlay.c @@ -105,19 +105,16 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc, if (bank_mask & 1) { /* Light up the button if pressed */ - if (input_state(port, RETRO_DEVICE_JOYPAD, 0, id)) + if (!input_state(port, RETRO_DEVICE_JOYPAD, 0, id)) { - all_buttons_pressed = true; - desc->updated = true; - } - else - { - /*we need ALL of the inputs to be active*/ - desc->updated = false; - - /*abort*/ + /* We need ALL of the inputs to be active, + * abort. */ + desc->updated = false; return false; } + + all_buttons_pressed = true; + desc->updated = true; } bank_mask >>= 1; From d0b735735c76c4b36be17e6bfd76fc560edf0ddc Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 8 Apr 2018 23:23:01 +0200 Subject: [PATCH 194/517] Update libretro-common --- libretro-common/include/libretro_d3d.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretro-common/include/libretro_d3d.h b/libretro-common/include/libretro_d3d.h index 2ed71aa7c0..7c44d8d52d 100644 --- a/libretro-common/include/libretro_d3d.h +++ b/libretro-common/include/libretro_d3d.h @@ -1,7 +1,7 @@ /* Copyright (C) 2010-2016 The RetroArch team * * --------------------------------------------------------------------------------------------- - * The following license statement only applies to this libretro API header (libretro_vulkan.h) + * The following license statement only applies to this libretro API header (libretro_d3d.h) * --------------------------------------------------------------------------------------------- * * Permission is hereby granted, free of charge, From 066d1815351ba44a82a1e66efbd16a9b8d16991d Mon Sep 17 00:00:00 2001 From: radius Date: Sun, 8 Apr 2018 16:58:30 -0500 Subject: [PATCH 195/517] cleanup --- input/input_remapping.c | 1 - 1 file changed, 1 deletion(-) diff --git a/input/input_remapping.c b/input/input_remapping.c index ee1d13fd2f..96ffd40557 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -298,7 +298,6 @@ void input_remapping_set_defaults(bool deinit) } for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) { - RARCH_LOG("******************User: %d Val: %d\n", i, j ); settings->uints.input_remap_ids[i][j] = j; } From 215868f7553907553b77bbc2b21b8d9fc8591aeb Mon Sep 17 00:00:00 2001 From: radius Date: Sun, 8 Apr 2018 17:21:17 -0500 Subject: [PATCH 196/517] add comment --- input/input_mapper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/input/input_mapper.c b/input/input_mapper.c index 1ccb42a975..1a1a13f00a 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -125,6 +125,8 @@ void input_mapper_poll(input_mapper_t *handle) 0, 0, RETRO_DEVICE_KEYBOARD); key_event[j] = true; } + /* key_event tracks if a key is pressed for ANY PLAYER, so we must check + if the key was used by any player before releasing */ else if (!key_event[j]) { input_keyboard_event(false, From 2cb14e730faa5c5d12e883989c1f220d1d0e2d6d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 00:21:22 +0200 Subject: [PATCH 197/517] Initialize to false by default --- input/input_mapper.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index 1ccb42a975..48cb8d3643 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -85,9 +85,10 @@ void input_mapper_poll(input_mapper_t *handle) { unsigned i, j; input_bits_t current_input; - settings_t *settings = config_get_ptr(); - unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); - bool key_event[RARCH_CUSTOM_BIND_LIST_END]; + settings_t *settings = config_get_ptr(); + unsigned max_users = + *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); + bool key_event[RARCH_CUSTOM_BIND_LIST_END] = { false }; #ifdef HAVE_MENU if (menu_driver_is_alive()) From 7b5a0bf7062abc0a7b16cb2643d73b6305c4a9a4 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 00:30:45 +0200 Subject: [PATCH 198/517] Move clear operation outside of keys_pressed functions --- input/input_driver.c | 4 ---- retroarch.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 8cd95fe416..7e0f1f9446 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -866,8 +866,6 @@ void input_menu_keys_pressed(void *data, input_bits_t *p_new_state) joypad_info.joy_idx = 0; joypad_info.auto_binds = NULL; - BIT256_CLEAR_ALL_PTR(p_new_state); - input_driver_block_libretro_input = false; input_driver_block_hotkey = false; @@ -1072,8 +1070,6 @@ void input_keys_pressed(void *data, input_bits_t *p_new_state) const struct retro_keybind *enable_hotkey = &input_config_binds[0][RARCH_ENABLE_HOTKEY]; bool game_focus_toggle_valid = false; - BIT256_CLEAR_ALL_PTR(p_new_state); - joypad_info.joy_idx = settings->uints.input_joypad_map[0]; joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx]; joypad_info.axis_threshold = input_driver_axis_threshold; diff --git a/retroarch.c b/retroarch.c index 2bc1c37638..fed3ebddc4 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2446,7 +2446,11 @@ static enum runloop_state runloop_check_state( #ifdef HAVE_MENU bool menu_driver_binding_state = menu_driver_is_binding_state(); bool menu_is_alive = menu_driver_is_alive(); +#endif + BIT256_CLEAR_ALL_PTR(¤t_input); + +#ifdef HAVE_MENU if (menu_is_alive && !(settings->bools.menu_unified_controls && !menu_input_dialog_get_display_kb())) input_menu_keys_pressed(settings, ¤t_input); else From a5c7b7984244f0d553fc0d50db7f393a81923a89 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 00:34:07 +0200 Subject: [PATCH 199/517] Move BIT256_CLEAR_ALL_PTR outside of function --- input/input_driver.c | 4 +--- input/input_mapper.c | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 7e0f1f9446..414e8be1f0 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1133,8 +1133,6 @@ void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_sta settings_t *settings = (settings_t*)data; const input_device_driver_t *joypad_driver = input_driver_get_joypad_driver(); - BIT256_CLEAR_ALL_PTR(p_new_state); - joypad_info.joy_idx = settings->uints.input_joypad_map[port]; joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx]; joypad_info.axis_threshold = input_driver_axis_threshold; @@ -1159,7 +1157,7 @@ void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_sta joypad_info, port, i, j, libretro_input_binds[port]); if (val >= 0) - p_new_state->analogs[offset] = val; + p_new_state->analogs[offset] = val; else p_new_state->analogs[offset+1] = val; } diff --git a/input/input_mapper.c b/input/input_mapper.c index 4cc6e17b61..253ffcf112 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -106,6 +106,7 @@ void input_mapper_poll(input_mapper_t *handle) { /* keyboard to gamepad remapping */ case RETRO_DEVICE_KEYBOARD: + BIT256_CLEAR_ALL_PTR(¤t_input); input_get_state_for_port(settings, i, ¤t_input); for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++) { @@ -147,6 +148,7 @@ void input_mapper_poll(input_mapper_t *handle) * the bit on the mapper input bitmap, later on the * original input is cleared in input_state */ BIT256_CLEAR_ALL(handle->buttons[i]); + BIT256_CLEAR_ALL_PTR(¤t_input); for (j = 0; j < 8; j++) handle->analog_value[i][j] = 0; From be5057eafc1c4ca8e3c569d7b5d5ef38ddb30801 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 00:38:44 +0200 Subject: [PATCH 200/517] Cleanups --- input/input_driver.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 414e8be1f0..51a7b04877 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1065,11 +1065,6 @@ void input_keys_pressed(void *data, input_bits_t *p_new_state) const struct retro_keybind *binds_auto = &input_autoconf_binds[0][RARCH_ENABLE_HOTKEY]; const struct retro_keybind *binds_norm = &binds[RARCH_ENABLE_HOTKEY]; - const struct retro_keybind *focus_binds_auto = &input_autoconf_binds[0][RARCH_GAME_FOCUS_TOGGLE]; - const struct retro_keybind *focus_normal = &binds[RARCH_GAME_FOCUS_TOGGLE]; - const struct retro_keybind *enable_hotkey = &input_config_binds[0][RARCH_ENABLE_HOTKEY]; - bool game_focus_toggle_valid = false; - joypad_info.joy_idx = settings->uints.input_joypad_map[0]; joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx]; joypad_info.axis_threshold = input_driver_axis_threshold; @@ -1083,7 +1078,10 @@ void input_keys_pressed(void *data, input_bits_t *p_new_state) if (check_input_driver_block_hotkey(binds_norm, binds_auto)) { - if ( enable_hotkey->valid + const struct retro_keybind *enable_hotkey = + &input_config_binds[0][RARCH_ENABLE_HOTKEY]; + + if ( enable_hotkey && enable_hotkey->valid && current_input->input_state( current_input_data, joypad_info, &binds, 0, RETRO_DEVICE_JOYPAD, 0, RARCH_ENABLE_HOTKEY)) @@ -1092,16 +1090,22 @@ void input_keys_pressed(void *data, input_bits_t *p_new_state) input_driver_block_hotkey = true; } - game_focus_toggle_valid = binds[RARCH_GAME_FOCUS_TOGGLE].valid; - - /* Allows rarch_focus_toggle hotkey to still work - * even though every hotkey is blocked */ - if (check_input_driver_block_hotkey( - focus_normal, focus_binds_auto) && game_focus_toggle_valid) + if (binds[RARCH_GAME_FOCUS_TOGGLE].valid) { - if (current_input->input_state(current_input_data, joypad_info, &binds, 0, - RETRO_DEVICE_JOYPAD, 0, RARCH_GAME_FOCUS_TOGGLE)) - input_driver_block_hotkey = false; + const struct retro_keybind *focus_binds_auto = + &input_autoconf_binds[0][RARCH_GAME_FOCUS_TOGGLE]; + const struct retro_keybind *focus_normal = + &binds[RARCH_GAME_FOCUS_TOGGLE]; + + /* Allows rarch_focus_toggle hotkey to still work + * even though every hotkey is blocked */ + if (check_input_driver_block_hotkey( + focus_normal, focus_binds_auto)) + { + if (current_input->input_state(current_input_data, joypad_info, &binds, 0, + RETRO_DEVICE_JOYPAD, 0, RARCH_GAME_FOCUS_TOGGLE)) + input_driver_block_hotkey = false; + } } for (i = 0; i < RARCH_BIND_LIST_END; i++) @@ -1141,7 +1145,8 @@ void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_sta { bool bit_pressed = false; - if (input_driver_input_state(joypad_info, libretro_input_binds, port, RETRO_DEVICE_JOYPAD, 0, i) != 0) + if (input_driver_input_state(joypad_info, libretro_input_binds, + port, RETRO_DEVICE_JOYPAD, 0, i) != 0) bit_pressed = true; if (bit_pressed) From 4db665130c2e0dabf7ff5b004c058e5e1e8fc2f5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 03:48:08 +0200 Subject: [PATCH 201/517] Start moving callback functions to their dedicated files --- menu/cbs/menu_cbs_left.c | 251 ++++++++++++++++++++++++++++ menu/cbs/menu_cbs_ok.c | 101 ++++++++++++ menu/menu_cbs.h | 30 ++++ menu/menu_setting.c | 344 +-------------------------------------- 4 files changed, 389 insertions(+), 337 deletions(-) diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 902c393fe6..04a403ca2e 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -35,6 +35,9 @@ #include "../../core_info.h" #include "../../managers/cheat_manager.h" #include "../../file_path_special.h" +#include "../../driver.h" +#include "../../audio/audio_driver.h" +#include "../../gfx/video_driver.h" #include "../../retroarch.h" #include "../../network/netplay/netplay.h" @@ -48,6 +51,254 @@ extern struct key_desc key_descriptors[RARCH_MAX_KEYS]; +int setting_action_left_analog_dpad_mode(void *data, bool wraparound) +{ + unsigned port = 0; + rarch_setting_t *setting = (rarch_setting_t*)data; + settings_t *settings = config_get_ptr(); + + if (!setting) + return -1; + + port = setting->index_offset; + + configuration_set_uint(settings, settings->uints.input_analog_dpad_mode[port], + (settings->uints.input_analog_dpad_mode + [port] + ANALOG_DPAD_LAST - 1) % ANALOG_DPAD_LAST); + + return 0; +} + +int setting_action_left_libretro_device_type( + void *data, bool wraparound) +{ + retro_ctx_controller_info_t pad; + unsigned current_device, current_idx, i, devices[128], + types = 0, port = 0; + const struct retro_controller_info *desc = NULL; + rarch_setting_t *setting = (rarch_setting_t*)data; + rarch_system_info_t *system = NULL; + + if (!setting) + return -1; + + port = setting->index_offset; + + devices[types++] = RETRO_DEVICE_NONE; + devices[types++] = RETRO_DEVICE_JOYPAD; + + system = runloop_get_system_info(); + + if (system) + { + /* Only push RETRO_DEVICE_ANALOG as default if we use an + * older core which doesn't use SET_CONTROLLER_INFO. */ + if (!system->ports.size) + devices[types++] = RETRO_DEVICE_ANALOG; + + if (port < system->ports.size) + desc = &system->ports.data[port]; + } + + if (desc) + { + for (i = 0; i < desc->num_types; i++) + { + unsigned id = desc->types[i].id; + if (types < ARRAY_SIZE(devices) && + id != RETRO_DEVICE_NONE && + id != RETRO_DEVICE_JOYPAD) + devices[types++] = id; + } + } + + current_device = input_config_get_device(port); + current_idx = 0; + for (i = 0; i < types; i++) + { + if (current_device != devices[i]) + continue; + + current_idx = i; + break; + } + + current_device = devices + [(current_idx + types - 1) % types]; + + input_config_set_device(port, current_device); + + pad.port = port; + pad.device = current_device; + + core_set_controller_port_device(&pad); + + return 0; +} + +int setting_action_left_bind_device(void *data, bool wraparound) +{ + unsigned *p = NULL; + unsigned index_offset = 0; + unsigned max_devices = input_config_get_device_count(); + rarch_setting_t *setting = (rarch_setting_t*)data; + settings_t *settings = config_get_ptr(); + + if (!setting || max_devices == 0) + return -1; + + index_offset = setting->index_offset; + + p = &settings->uints.input_joypad_map[index_offset]; + + if ((*p) >= max_devices) + *p = max_devices - 1; + else if ((*p) > 0) + (*p)--; + + return 0; +} + +int setting_action_left_mouse_index(void *data, bool wraparound) +{ + rarch_setting_t *setting = (rarch_setting_t*)data; + settings_t *settings = config_get_ptr(); + + if (!setting) + return -1; + + if (settings->uints.input_mouse_index[setting->index_offset]) + { + --settings->uints.input_mouse_index[setting->index_offset]; + settings->modified = true; + } + + return 0; +} + +int setting_uint_action_left_custom_viewport_width( + void *data, bool wraparound) +{ + video_viewport_t vp; + struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); + video_viewport_t *custom = video_viewport_get_custom(); + settings_t *settings = config_get_ptr(); + struct retro_game_geometry *geom = (struct retro_game_geometry*) + &av_info->geometry; + + if (!settings || !av_info) + return -1; + + video_driver_get_viewport_info(&vp); + + if (custom->width <= 1) + custom->width = 1; + else if (settings->bools.video_scale_integer) + { + if (custom->width > geom->base_width) + custom->width -= geom->base_width; + } + else + custom->width -= 1; + + aspectratio_lut[ASPECT_RATIO_CUSTOM].value = + (float)custom->width / custom->height; + + return 0; +} + +int setting_uint_action_left_custom_viewport_height( + void *data, bool wraparound) +{ + video_viewport_t vp; + struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); + video_viewport_t *custom = video_viewport_get_custom(); + settings_t *settings = config_get_ptr(); + struct retro_game_geometry *geom = (struct retro_game_geometry*) + &av_info->geometry; + + if (!settings || !av_info) + return -1; + + video_driver_get_viewport_info(&vp); + + if (custom->height <= 1) + custom->height = 1; + else if (settings->bools.video_scale_integer) + { + if (custom->height > geom->base_height) + custom->height -= geom->base_height; + } + else + custom->height -= 1; + + aspectratio_lut[ASPECT_RATIO_CUSTOM].value = + (float)custom->width / custom->height; + + return 0; +} + +int setting_string_action_left_audio_device( + void *data, bool wraparound) +{ +#if !defined(RARCH_CONSOLE) + int audio_device_index; + struct string_list *ptr = NULL; + rarch_setting_t *setting = (rarch_setting_t*)data; + + if (!audio_driver_get_devices_list((void**)&ptr)) + return -1; + + if (!ptr) + return -1; + + /* Get index in the string list */ + audio_device_index = string_list_find_elem( + ptr, setting->value.target.string) - 1; + audio_device_index--; + + /* Reset index if needed */ + if (audio_device_index < 0) + audio_device_index = (int)(ptr->size - 1); + + strlcpy(setting->value.target.string, ptr->elems[audio_device_index].data, setting->size); +#endif + + return 0; +} + +int setting_string_action_left_driver(void *data, + bool wraparound) +{ + driver_ctx_info_t drv; + rarch_setting_t *setting = (rarch_setting_t*)data; + + if (!setting) + return -1; + + drv.label = setting->name; + drv.s = setting->value.target.string; + drv.len = setting->size; + + if (!driver_ctl(RARCH_DRIVER_CTL_FIND_PREV, &drv)) + { + settings_t *settings = config_get_ptr(); + + if (settings && settings->bools.menu_navigation_wraparound_enable) + { + drv.label = setting->name; + drv.s = setting->value.target.string; + drv.len = setting->size; + driver_ctl(RARCH_DRIVER_CTL_FIND_LAST, &drv); + } + } + + if (setting->change_handler) + setting->change_handler(setting); + + return 0; +} + static int generic_shader_action_parameter_left( struct video_shader_parameter *param, unsigned type, const char *label, bool wraparound) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 3f1915decc..6b22dbd473 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -26,13 +26,21 @@ #include "../../config.h" #endif +#include "../../config.def.h" +#include "../../config.def.keybinds.h" +#include "../../wifi/wifi_driver.h" +#include "../../driver.h" + #include "../menu_driver.h" #include "../menu_cbs.h" #include "../menu_setting.h" #include "../menu_shader.h" #include "../widgets/menu_dialog.h" +#include "../widgets/menu_entry.h" #include "../widgets/menu_filebrowser.h" #include "../widgets/menu_input_dialog.h" +#include "../widgets/menu_input_bind_dialog.h" +#include "../menu_input.h" #include "../menu_networking.h" #include "../menu_content.h" #include "../menu_shader.h" @@ -121,6 +129,99 @@ static char *lakka_get_project(void) info.enum_idx = a; \ dl_type = b; +int setting_action_ok_video_refresh_rate_auto(void *data, bool wraparound) +{ + double video_refresh_rate = 0.0; + double deviation = 0.0; + unsigned sample_points = 0; + rarch_setting_t *setting = (rarch_setting_t*)data; + + if (!setting) + return -1; + + if (video_monitor_fps_statistics(&video_refresh_rate, + &deviation, &sample_points)) + { + float video_refresh_rate_float = (float)video_refresh_rate; + driver_ctl(RARCH_DRIVER_CTL_SET_REFRESH_RATE, &video_refresh_rate_float); + /* Incase refresh rate update forced non-block video. */ + command_event(CMD_EVENT_VIDEO_SET_BLOCKING_STATE, NULL); + } + + if (setting_generic_action_ok_default(setting, wraparound) != 0) + return -1; + + return 0; +} + +int setting_action_ok_bind_all(void *data, bool wraparound) +{ + (void)wraparound; + if (!menu_input_key_bind_set_mode(MENU_INPUT_BINDS_CTL_BIND_ALL, data)) + return -1; + return 0; +} + +int setting_action_ok_bind_all_save_autoconfig(void *data, + bool wraparound) +{ + unsigned index_offset; + rarch_setting_t *setting = (rarch_setting_t*)data; + const char *name = NULL; + + (void)wraparound; + + if (!setting) + return -1; + + index_offset = setting->index_offset; + name = input_config_get_device_name(index_offset); + + if(!string_is_empty(name) && config_save_autoconf_profile(name, index_offset)) + runloop_msg_queue_push( + msg_hash_to_str(MSG_AUTOCONFIG_FILE_SAVED_SUCCESSFULLY), 1, 100, true); + else + runloop_msg_queue_push( + msg_hash_to_str(MSG_AUTOCONFIG_FILE_ERROR_SAVING), 1, 100, true); + + + return 0; +} + +int setting_action_ok_bind_defaults(void *data, bool wraparound) +{ + unsigned i; + menu_input_ctx_bind_limits_t lim; + struct retro_keybind *target = NULL; + const struct retro_keybind *def_binds = NULL; + rarch_setting_t *setting = (rarch_setting_t*)data; + + (void)wraparound; + + if (!setting) + return -1; + + target = &input_config_binds[setting->index_offset][0]; + def_binds = (setting->index_offset) ? + retro_keybinds_rest : retro_keybinds_1; + + lim.min = MENU_SETTINGS_BIND_BEGIN; + lim.max = MENU_SETTINGS_BIND_LAST; + + menu_input_key_bind_set_min_max(&lim); + + for (i = MENU_SETTINGS_BIND_BEGIN; + i <= MENU_SETTINGS_BIND_LAST; i++, target++) + { + target->key = def_binds[i - MENU_SETTINGS_BIND_BEGIN].key; + target->joykey = NO_BTN; + target->joyaxis = AXIS_NONE; + target->mbutton = NO_BTN; + } + + return 0; +} + static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) { switch (lbl) diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index a6e629394e..3be1f16e85 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -161,6 +161,36 @@ int action_right_input_desc_kbd(unsigned type, const char *label, int action_right_cheat(unsigned type, const char *label, bool wraparound); +int setting_action_ok_video_refresh_rate_auto(void *data, bool wraparound); + +int setting_action_ok_bind_all(void *data, bool wraparound); + +int setting_action_ok_bind_all_save_autoconfig(void *data, + bool wraparound); + +int setting_action_ok_bind_defaults(void *data, bool wraparound); + +int setting_action_left_analog_dpad_mode(void *data, bool wraparound); + +int setting_action_left_libretro_device_type( + void *data, bool wraparound); + +int setting_action_left_bind_device(void *data, bool wraparound); + +int setting_action_left_mouse_index(void *data, bool wraparound); + +int setting_uint_action_left_custom_viewport_width( + void *data, bool wraparound); + +int setting_uint_action_left_custom_viewport_height( + void *data, bool wraparound); + +int setting_string_action_left_driver(void *data, + bool wraparound); + +int setting_string_action_left_audio_device( + void *data, bool wraparound); + /* End of function callbacks */ int menu_cbs_init_bind_left(menu_file_list_cbs_t *cbs, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index fd3ecddf86..b785ed44b8 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -55,6 +55,7 @@ #include "widgets/menu_input_bind_dialog.h" #include "menu_setting.h" +#include "menu_cbs.h" #include "menu_driver.h" #include "menu_animation.h" #include "menu_input.h" @@ -234,37 +235,8 @@ static void setting_get_string_representation_int_audio_wasapi_sh_buffer_length( } #endif -static int setting_uint_action_left_custom_viewport_width(void *data, bool wraparound) -{ - video_viewport_t vp; - struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); - video_viewport_t *custom = video_viewport_get_custom(); - settings_t *settings = config_get_ptr(); - struct retro_game_geometry *geom = (struct retro_game_geometry*) - &av_info->geometry; - - if (!settings || !av_info) - return -1; - - video_driver_get_viewport_info(&vp); - - if (custom->width <= 1) - custom->width = 1; - else if (settings->bools.video_scale_integer) - { - if (custom->width > geom->base_width) - custom->width -= geom->base_width; - } - else - custom->width -= 1; - - aspectratio_lut[ASPECT_RATIO_CUSTOM].value = - (float)custom->width / custom->height; - - return 0; -} - -static int setting_uint_action_right_custom_viewport_width(void *data, bool wraparound) +static int setting_uint_action_right_custom_viewport_width( + void *data, bool wraparound) { video_viewport_t vp; struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); @@ -289,37 +261,8 @@ static int setting_uint_action_right_custom_viewport_width(void *data, bool wrap return 0; } -static int setting_uint_action_left_custom_viewport_height(void *data, bool wraparound) -{ - video_viewport_t vp; - struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); - video_viewport_t *custom = video_viewport_get_custom(); - settings_t *settings = config_get_ptr(); - struct retro_game_geometry *geom = (struct retro_game_geometry*) - &av_info->geometry; - - if (!settings || !av_info) - return -1; - - video_driver_get_viewport_info(&vp); - - if (custom->height <= 1) - custom->height = 1; - else if (settings->bools.video_scale_integer) - { - if (custom->height > geom->base_height) - custom->height -= geom->base_height; - } - else - custom->height -= 1; - - aspectratio_lut[ASPECT_RATIO_CUSTOM].value = - (float)custom->width / custom->height; - - return 0; -} - -static int setting_uint_action_right_custom_viewport_height(void *data, bool wraparound) +static int setting_uint_action_right_custom_viewport_height( + void *data, bool wraparound) { video_viewport_t vp; struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); @@ -345,32 +288,8 @@ static int setting_uint_action_right_custom_viewport_height(void *data, bool wra } #if !defined(RARCH_CONSOLE) -static int setting_string_action_left_audio_device(void *data, bool wraparound) -{ - int audio_device_index; - struct string_list *ptr = NULL; - rarch_setting_t *setting = (rarch_setting_t*)data; - - if (!audio_driver_get_devices_list((void**)&ptr)) - return -1; - - if (!ptr) - return -1; - - /* Get index in the string list */ - audio_device_index = string_list_find_elem(ptr,setting->value.target.string) - 1; - audio_device_index--; - - /* Reset index if needed */ - if (audio_device_index < 0) - audio_device_index = (int)(ptr->size - 1); - - strlcpy(setting->value.target.string, ptr->elems[audio_device_index].data, setting->size); - - return 0; -} - -static int setting_string_action_right_audio_device(void *data, bool wraparound) +static int setting_string_action_right_audio_device( + void *data, bool wraparound) { int audio_device_index; struct string_list *ptr = NULL; @@ -396,38 +315,6 @@ static int setting_string_action_right_audio_device(void *data, bool wraparound) } #endif -static int setting_string_action_left_driver(void *data, - bool wraparound) -{ - driver_ctx_info_t drv; - rarch_setting_t *setting = (rarch_setting_t*)data; - - if (!setting) - return -1; - - drv.label = setting->name; - drv.s = setting->value.target.string; - drv.len = setting->size; - - if (!driver_ctl(RARCH_DRIVER_CTL_FIND_PREV, &drv)) - { - settings_t *settings = config_get_ptr(); - - if (settings && settings->bools.menu_navigation_wraparound_enable) - { - drv.label = setting->name; - drv.s = setting->value.target.string; - drv.len = setting->size; - driver_ctl(RARCH_DRIVER_CTL_FIND_LAST, &drv); - } - } - - if (setting->change_handler) - setting->change_handler(setting); - - return 0; -} - static int setting_string_action_right_driver(void *data, bool wraparound) { @@ -1145,24 +1032,6 @@ static int setting_action_start_video_refresh_rate_auto( ******* ACTION TOGGLE CALLBACK FUNCTIONS ******* **/ -static int setting_action_left_analog_dpad_mode(void *data, bool wraparound) -{ - unsigned port = 0; - rarch_setting_t *setting = (rarch_setting_t*)data; - settings_t *settings = config_get_ptr(); - - if (!setting) - return -1; - - port = setting->index_offset; - - configuration_set_uint(settings, settings->uints.input_analog_dpad_mode[port], - (settings->uints.input_analog_dpad_mode - [port] + ANALOG_DPAD_LAST - 1) % ANALOG_DPAD_LAST); - - return 0; -} - static int setting_action_right_analog_dpad_mode(void *data, bool wraparound) { unsigned port = 0; @@ -1182,73 +1051,6 @@ static int setting_action_right_analog_dpad_mode(void *data, bool wraparound) return 0; } -static int setting_action_left_libretro_device_type( - void *data, bool wraparound) -{ - retro_ctx_controller_info_t pad; - unsigned current_device, current_idx, i, devices[128], - types = 0, port = 0; - const struct retro_controller_info *desc = NULL; - rarch_setting_t *setting = (rarch_setting_t*)data; - rarch_system_info_t *system = NULL; - - if (!setting) - return -1; - - port = setting->index_offset; - - devices[types++] = RETRO_DEVICE_NONE; - devices[types++] = RETRO_DEVICE_JOYPAD; - - system = runloop_get_system_info(); - - if (system) - { - /* Only push RETRO_DEVICE_ANALOG as default if we use an - * older core which doesn't use SET_CONTROLLER_INFO. */ - if (!system->ports.size) - devices[types++] = RETRO_DEVICE_ANALOG; - - if (port < system->ports.size) - desc = &system->ports.data[port]; - } - - if (desc) - { - for (i = 0; i < desc->num_types; i++) - { - unsigned id = desc->types[i].id; - if (types < ARRAY_SIZE(devices) && - id != RETRO_DEVICE_NONE && - id != RETRO_DEVICE_JOYPAD) - devices[types++] = id; - } - } - - current_device = input_config_get_device(port); - current_idx = 0; - for (i = 0; i < types; i++) - { - if (current_device != devices[i]) - continue; - - current_idx = i; - break; - } - - current_device = devices - [(current_idx + types - 1) % types]; - - input_config_set_device(port, current_device); - - pad.port = port; - pad.device = current_device; - - core_set_controller_port_device(&pad); - - return 0; -} - static int setting_action_right_libretro_device_type( void *data, bool wraparound) { @@ -1314,29 +1116,6 @@ static int setting_action_right_libretro_device_type( return 0; } -static int setting_action_left_bind_device(void *data, bool wraparound) -{ - unsigned *p = NULL; - unsigned index_offset = 0; - unsigned max_devices = input_config_get_device_count(); - rarch_setting_t *setting = (rarch_setting_t*)data; - settings_t *settings = config_get_ptr(); - - if (!setting || max_devices == 0) - return -1; - - index_offset = setting->index_offset; - - p = &settings->uints.input_joypad_map[index_offset]; - - if ((*p) >= max_devices) - *p = max_devices - 1; - else if ((*p) > 0) - (*p)--; - - return 0; -} - static int setting_action_right_bind_device(void *data, bool wraparound) { unsigned index_offset; @@ -1358,23 +1137,6 @@ static int setting_action_right_bind_device(void *data, bool wraparound) return 0; } -static int setting_action_left_mouse_index(void *data, bool wraparound) -{ - rarch_setting_t *setting = (rarch_setting_t*)data; - settings_t *settings = config_get_ptr(); - - if (!setting) - return -1; - - if (settings->uints.input_mouse_index[setting->index_offset]) - { - --settings->uints.input_mouse_index[setting->index_offset]; - settings->modified = true; - } - - return 0; -} - static int setting_action_right_mouse_index(void *data, bool wraparound) { rarch_setting_t *setting = (rarch_setting_t*)data; @@ -1393,73 +1155,6 @@ static int setting_action_right_mouse_index(void *data, bool wraparound) ******* ACTION OK CALLBACK FUNCTIONS ******* **/ -static int setting_action_ok_bind_all(void *data, bool wraparound) -{ - (void)wraparound; - if (!menu_input_key_bind_set_mode(MENU_INPUT_BINDS_CTL_BIND_ALL, data)) - return -1; - return 0; -} - -static int setting_action_ok_bind_all_save_autoconfig(void *data, bool wraparound) -{ - unsigned index_offset; - rarch_setting_t *setting = (rarch_setting_t*)data; - const char *name = NULL; - - (void)wraparound; - - if (!setting) - return -1; - - index_offset = setting->index_offset; - name = input_config_get_device_name(index_offset); - - if(!string_is_empty(name) && config_save_autoconf_profile(name, index_offset)) - runloop_msg_queue_push( - msg_hash_to_str(MSG_AUTOCONFIG_FILE_SAVED_SUCCESSFULLY), 1, 100, true); - else - runloop_msg_queue_push( - msg_hash_to_str(MSG_AUTOCONFIG_FILE_ERROR_SAVING), 1, 100, true); - - - return 0; -} - -static int setting_action_ok_bind_defaults(void *data, bool wraparound) -{ - unsigned i; - menu_input_ctx_bind_limits_t lim; - struct retro_keybind *target = NULL; - const struct retro_keybind *def_binds = NULL; - rarch_setting_t *setting = (rarch_setting_t*)data; - - (void)wraparound; - - if (!setting) - return -1; - - target = &input_config_binds[setting->index_offset][0]; - def_binds = (setting->index_offset) ? - retro_keybinds_rest : retro_keybinds_1; - - lim.min = MENU_SETTINGS_BIND_BEGIN; - lim.max = MENU_SETTINGS_BIND_LAST; - - menu_input_key_bind_set_min_max(&lim); - - for (i = MENU_SETTINGS_BIND_BEGIN; - i <= MENU_SETTINGS_BIND_LAST; i++, target++) - { - target->key = def_binds[i - MENU_SETTINGS_BIND_BEGIN].key; - target->joykey = NO_BTN; - target->joyaxis = AXIS_NONE; - target->mbutton = NO_BTN; - } - - return 0; -} - static void setting_get_string_representation_st_float_video_refresh_rate_auto( void *data, char *s, size_t len) @@ -1482,31 +1177,6 @@ setting_get_string_representation_st_float_video_refresh_rate_auto( strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE), len); } -static int setting_action_ok_video_refresh_rate_auto(void *data, bool wraparound) -{ - double video_refresh_rate = 0.0; - double deviation = 0.0; - unsigned sample_points = 0; - rarch_setting_t *setting = (rarch_setting_t*)data; - - if (!setting) - return -1; - - if (video_monitor_fps_statistics(&video_refresh_rate, - &deviation, &sample_points)) - { - float video_refresh_rate_float = (float)video_refresh_rate; - driver_ctl(RARCH_DRIVER_CTL_SET_REFRESH_RATE, &video_refresh_rate_float); - /* Incase refresh rate update forced non-block video. */ - command_event(CMD_EVENT_VIDEO_SET_BLOCKING_STATE, NULL); - } - - if (setting_generic_action_ok_default(setting, wraparound) != 0) - return -1; - - return 0; -} - static void get_string_representation_bind_device(void * data, char *s, size_t len) { From f2c23970a34a09667c1612f0927c01fdd8757829 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Mon, 9 Apr 2018 05:56:58 +0200 Subject: [PATCH 202/517] (font_driver.c) silences the xcode warnings --- gfx/font_driver.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gfx/font_driver.c b/gfx/font_driver.c index 63e4333dd7..75a5adb2f5 100644 --- a/gfx/font_driver.c +++ b/gfx/font_driver.c @@ -693,10 +693,10 @@ static INLINE unsigned font_get_replacement(const char* src, const char* start) static char* font_driver_reshape_msg(const char* msg) { /* worst case transformations are 2 bytes to 4 bytes */ - char* buffer = (char*)malloc((strlen(msg) * 2) + 1); - const char* src = msg; - char* dst = buffer; - bool reverse = false; + unsigned char* buffer = (unsigned char*)malloc((strlen(msg) * 2) + 1); + const unsigned char* src = (const unsigned char*)msg; + unsigned char* dst = (unsigned char*)buffer; + bool reverse = false; while (*src || reverse) { @@ -708,7 +708,7 @@ static char* font_driver_reshape_msg(const char* msg) if (IS_RTL(src) || IS_DIR_NEUTRAL(src)) { - unsigned replacement = font_get_replacement(src, msg); + unsigned replacement = font_get_replacement((const char*)src, msg); if (replacement) { if (replacement < 0x80) @@ -770,7 +770,7 @@ static char* font_driver_reshape_msg(const char* msg) *dst = '\0'; - return buffer; + return (char*)buffer; } #endif From 91ba0765e6c65ae086fb97d3e9ac5292b9a8c93f Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Mon, 9 Apr 2018 05:58:32 +0200 Subject: [PATCH 203/517] (libchdr) Silnces xcode warning --- libretro-common/formats/libchdr/chd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libretro-common/formats/libchdr/chd.c b/libretro-common/formats/libchdr/chd.c index 78cac091bb..be359615a1 100644 --- a/libretro-common/formats/libchdr/chd.c +++ b/libretro-common/formats/libchdr/chd.c @@ -405,9 +405,10 @@ void *lzma_fast_alloc(void *p, size_t size) } /* alloc a new one and put it into the list */ - addr = (uint32_t *)malloc(sizeof(uint8_t) * (size + sizeof(uint32_t))); - if (addr==NULL) + addr = (uint32_t *)malloc(sizeof(uint32_t) * (size + sizeof(uint32_t))); + if (!addr) return NULL; + for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) { if (codec->allocptr[scan] == NULL) From 08a54e45f2b7bde98fce6f0fdbbc474a394ee8c8 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Mon, 9 Apr 2018 15:56:45 +0200 Subject: [PATCH 204/517] Get rid of a lot of implicit conversions --- audio/audio_driver.c | 2 +- cheevos/cheevos.c | 6 +-- deps/stb/stb_vorbis.h | 2 +- gfx/drivers/gl.c | 2 +- gfx/drivers_shader/shader_glsl.c | 6 +-- libretro-common/audio/audio_mixer.c | 4 +- libretro-common/streams/file_stream.c | 4 +- .../streams/file_stream_transforms.c | 2 +- libretro-common/streams/interface_stream.c | 2 +- libretro-common/string/stdstring.c | 2 +- menu/drivers/materialui.c | 4 +- menu/drivers/xmb.c | 37 +++++++++---------- movie.c | 6 +-- network/netplay/netplay_delta.c | 6 +-- network/netplay/netplay_discovery.c | 5 +-- network/netplay/netplay_handshake.c | 12 +++--- network/netplay/netplay_io.c | 36 +++++++++--------- network/netplay/netplay_sync.c | 6 ++- runahead/run_ahead.c | 2 +- runahead/secondary_core.c | 8 ++-- tasks/task_audio_mixer.c | 2 +- tasks/task_database.c | 4 +- verbosity.c | 9 +---- 23 files changed, 84 insertions(+), 85 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index fb06d65ffb..4ef2d3ce54 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -213,7 +213,7 @@ bool compute_audio_buffer_statistics(audio_statistics_t *stats) if (!stats || samples < 3) return false; - stats->samples = audio_driver_free_samples_count; + stats->samples = (unsigned)audio_driver_free_samples_count; #ifdef WARPUP /* uint64 to double not implemented, fair chance diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 57f027641f..556ac9942d 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -1339,7 +1339,7 @@ static int cheevos_new_lboard(cheevos_readud_t *ud) if (!lboard) return -1; - lboard->id = strtol(ud->id.string, NULL, 10); + lboard->id = (unsigned)strtol(ud->id.string, NULL, 10); lboard->format = cheevos_parse_format(&ud->format); lboard->title = cheevos_dupstr(&ud->title); lboard->description = cheevos_dupstr(&ud->desc); @@ -3468,7 +3468,7 @@ found: if ((void*)coro->json) free((void*)coro->json); RARCH_LOG("[CHEEVOS]: got game id %s.\n", gameid); - coro->gameid = strtol(gameid, NULL, 10); + coro->gameid = (unsigned)strtol(gameid, NULL, 10); CORO_RET(); } @@ -3733,7 +3733,7 @@ found: coro->json[length] = 0; } - coro->k = length; + coro->k = (unsigned)length; net_http_delete(coro->http); net_http_connection_free(coro->conn); CORO_RET(); diff --git a/deps/stb/stb_vorbis.h b/deps/stb/stb_vorbis.h index 6adaff4940..be97f0b655 100644 --- a/deps/stb/stb_vorbis.h +++ b/deps/stb/stb_vorbis.h @@ -3278,7 +3278,7 @@ static stb_vorbis * vorbis_alloc(stb_vorbis *f) unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) { - return f->stream - f->stream_start; + return (unsigned int)(f->stream - f->stream_start); } #ifndef STB_VORBIS_NO_PULLDATA_API diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 720a4b238b..260f776582 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -747,7 +747,7 @@ static void gl_render_osd_background( float *verts = (float*)malloc(2 * vertices_total * sizeof(float)); settings_t *settings = config_get_ptr(); int msg_width = - font_driver_get_message_width(NULL, msg, strlen(msg), 1.0f); + font_driver_get_message_width(NULL, msg, (unsigned)strlen(msg), 1.0f); /* shader driver expects vertex coords as 0..1 */ float x = video_info->font_msg_pos_x; diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index 3f5d077d5c..7485cb341c 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -1486,9 +1486,9 @@ static bool gl_glsl_set_mvp(void *data, void *shader_data, const void *mat_data) #define gl_glsl_set_coord_array(attribs, coord1, coord2, coords, size, multiplier) \ unsigned y; \ - attribs[attribs_size].loc = coord1; \ - attribs[attribs_size].size = multiplier; \ - attribs[attribs_size].offset = size * sizeof(GLfloat); \ + attribs[attribs_size].loc = (GLint)coord1; \ + attribs[attribs_size].size = (GLsizei)multiplier; \ + attribs[attribs_size].offset = (GLsizei)(size * sizeof(GLfloat)); \ for (y = 0; y < (multiplier * coords->vertices); y++) \ buffer[y + size] = coord2[y]; \ size += multiplier * coords->vertices; \ diff --git a/libretro-common/audio/audio_mixer.c b/libretro-common/audio/audio_mixer.c index c781297309..0984fc2d87 100644 --- a/libretro-common/audio/audio_mixer.c +++ b/libretro-common/audio/audio_mixer.c @@ -667,7 +667,7 @@ static void audio_mixer_mix_ogg(float* buffer, size_t num_frames, int i; struct resampler_data info; float temp_buffer[AUDIO_MIXER_TEMP_OGG_BUFFER]; - unsigned buf_free = num_frames * 2; + unsigned buf_free = (unsigned)(num_frames * 2); unsigned temp_samples = 0; float* pcm = NULL; @@ -740,7 +740,7 @@ static void audio_mixer_mix_mod(float* buffer, size_t num_frames, float samplef = 0.0f; int samplei = 0; unsigned temp_samples = 0; - unsigned buf_free = num_frames * 2; + unsigned buf_free = (unsigned)(num_frames * 2); int* pcm = NULL; if (voice->types.mod.position == voice->types.mod.samples) diff --git a/libretro-common/streams/file_stream.c b/libretro-common/streams/file_stream.c index 988ba73028..aa3bda1c78 100644 --- a/libretro-common/streams/file_stream.c +++ b/libretro-common/streams/file_stream.c @@ -320,14 +320,14 @@ int filestream_putc(RFILE *stream, int c) int filestream_vprintf(RFILE *stream, const char* format, va_list args) { static char buffer[8 * 1024]; - int num_chars = vsprintf(buffer, format, args); + int64_t num_chars = vsprintf(buffer, format, args); if (num_chars < 0) return -1; else if (num_chars == 0) return 0; - return filestream_write(stream, buffer, num_chars); + return (int)filestream_write(stream, buffer, num_chars); } int filestream_printf(RFILE *stream, const char* format, ...) diff --git a/libretro-common/streams/file_stream_transforms.c b/libretro-common/streams/file_stream_transforms.c index 8796073693..143db89c03 100644 --- a/libretro-common/streams/file_stream_transforms.c +++ b/libretro-common/streams/file_stream_transforms.c @@ -93,7 +93,7 @@ int rfseek(RFILE* stream, long offset, int origin) break; } - return filestream_seek(stream, offset, seek_position); + return (int)filestream_seek(stream, (ssize_t)offset, seek_position); } size_t rfread(void* buffer, diff --git a/libretro-common/streams/interface_stream.c b/libretro-common/streams/interface_stream.c index c61d12234b..a219a1525d 100644 --- a/libretro-common/streams/interface_stream.c +++ b/libretro-common/streams/interface_stream.c @@ -435,7 +435,7 @@ intfstream_t *intfstream_open_memory(void *data, info.type = INTFSTREAM_MEMORY; info.memory.buf.data = (uint8_t*)data; - info.memory.buf.size = size; + info.memory.buf.size = (unsigned)size; info.memory.writable = false; fd = (intfstream_t*)intfstream_init(&info); diff --git a/libretro-common/string/stdstring.c b/libretro-common/string/stdstring.c index 02f8dad8ec..f864cea356 100644 --- a/libretro-common/string/stdstring.c +++ b/libretro-common/string/stdstring.c @@ -169,7 +169,7 @@ char *word_wrap(char* buffer, const char *string, int line_width, bool unicode) } character = utf8skip(&string[i], 1); - char_len = character - &string[i]; + char_len = (unsigned)(character - &string[i]); if (!unicode) counter += char_len - 1; diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index a821ff8ff3..8d4e0deb7b 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -631,7 +631,7 @@ static void materialui_render_messagebox(materialui_handle_t *mui, { longest = len; longest_width = font_driver_get_message_width( - mui->font, msg, strlen(msg), 1); + mui->font, msg, (unsigned)strlen(msg), 1); } } @@ -1630,7 +1630,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) ); } - ticker_limit = usable_width / mui->glyph_width; + ticker_limit = (unsigned)(usable_width / mui->glyph_width); ticker.s = title_buf; ticker.len = ticker_limit; diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 362709187e..cd8eca370a 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -468,7 +468,7 @@ static void xmb_free_node(xmb_node_t *node) */ static void xmb_free_list_nodes(file_list_t *list, bool actiondata) { - unsigned i, size = file_list_get_size(list); + unsigned i, size = (unsigned)file_list_get_size(list); for (i = 0; i < size; ++i) { @@ -935,7 +935,7 @@ static void xmb_render_messagebox_internal( { longest = len; longest_width = font_driver_get_message_width( - xmb->font, msg, strlen(msg), 1); + xmb->font, msg, (unsigned)strlen(msg), 1); } } @@ -1633,17 +1633,16 @@ static void xmb_push_animations(xmb_node_t *node, static void xmb_list_switch_old(xmb_handle_t *xmb, file_list_t *list, int dir, size_t current) { - unsigned i, first, last, height; - size_t end = file_list_get_size(list); - float ix = -xmb->icon_spacing_horizontal * dir; - float ia = 0; - - first = 0; - last = end > 0 ? end - 1 : 0; + unsigned i, height; + size_t end = file_list_get_size(list); + float ix = -xmb->icon_spacing_horizontal * dir; + float ia = 0; + unsigned first = 0; + unsigned last = (unsigned)(end > 0 ? end - 1 : 0); video_driver_get_size(NULL, &height); xmb_calculate_visible_range(xmb, height, end, - current, &first, &last); + (unsigned)current, &first, &last); for (i = 0; i < end; i++) { @@ -1714,10 +1713,10 @@ static void xmb_list_switch_new(xmb_handle_t *xmb, end = file_list_get_size(list); first = 0; - last = end > 0 ? end - 1 : 0; + last = (unsigned)(end > 0 ? end - 1 : 0); video_driver_get_size(NULL, &height); - xmb_calculate_visible_range(xmb, height, end, current, &first, &last); + xmb_calculate_visible_range(xmb, height, end, (unsigned)current, &first, &last); for (i = 0; i < end; i++) { @@ -2445,7 +2444,7 @@ static void xmb_calculate_visible_range(const xmb_handle_t *xmb, float base_y = xmb->margins_screen_top; *first = 0; - *last = list_size ? list_size - 1 : 0; + *last = (unsigned)(list_size ? list_size - 1 : 0); if (current) { @@ -2782,10 +2781,10 @@ static void xmb_draw_items( i = 0; } - first = i; - last = end - 1; + first = (unsigned)i; + last = (unsigned)(end - 1); - xmb_calculate_visible_range(xmb, height, end, current, &first, &last); + xmb_calculate_visible_range(xmb, height, end, (unsigned)current, &first, &last); menu_display_blend_begin(video_info); @@ -2834,18 +2833,18 @@ static void xmb_render(void *data, bool is_idle) if (pointer_enable || mouse_enable) { + unsigned height; size_t selection = menu_navigation_get_selection(); int16_t pointer_y = menu_input_pointer_state(MENU_POINTER_Y_AXIS); int16_t mouse_y = menu_input_mouse_state(MENU_MOUSE_Y_AXIS) + (xmb->cursor_size/2); unsigned first = 0, last = end; - unsigned height; video_driver_get_size(NULL, &height); if (height) xmb_calculate_visible_range(xmb, height, - end, selection, &first, &last); + end, (unsigned)selection, &first, &last); for (i = first; i <= last; i++) { @@ -4645,7 +4644,7 @@ static void xmb_list_cache(void *data, enum menu_list_type type, unsigned action xmb->selection_ptr_old = selection; xmb_calculate_visible_range(xmb, height, selection_buf->size, - xmb->selection_ptr_old, &first, &last); + (unsigned)xmb->selection_ptr_old, &first, &last); xmb_list_deep_copy(selection_buf, xmb->selection_buf_old, first, last); diff --git a/movie.c b/movie.c index f84635d176..0dc0da9860 100644 --- a/movie.c +++ b/movie.c @@ -290,7 +290,7 @@ static void bsv_movie_frame_rewind(bsv_movie_t *handle) { /* If we're at the beginning... */ handle->frame_ptr = 0; - intfstream_seek(handle->file, handle->min_file_pos, SEEK_SET); + intfstream_seek(handle->file, (int)handle->min_file_pos, SEEK_SET); } else { @@ -303,7 +303,7 @@ static void bsv_movie_frame_rewind(bsv_movie_t *handle) handle->frame_ptr = (handle->frame_ptr - (handle->first_rewind ? 1 : 2)) & handle->frame_mask; intfstream_seek(handle->file, - handle->frame_pos[handle->frame_ptr], SEEK_SET); + (int)handle->frame_pos[handle->frame_ptr], SEEK_SET); } if (intfstream_tell(handle->file) <= (long)handle->min_file_pos) @@ -327,7 +327,7 @@ static void bsv_movie_frame_rewind(bsv_movie_t *handle) intfstream_write(handle->file, handle->state, handle->state_size); } else - intfstream_seek(handle->file, handle->min_file_pos, SEEK_SET); + intfstream_seek(handle->file, (int)handle->min_file_pos, SEEK_SET); } } diff --git a/network/netplay/netplay_delta.c b/network/netplay/netplay_delta.c index d6b7efe58b..2b770075ee 100644 --- a/network/netplay/netplay_delta.c +++ b/network/netplay/netplay_delta.c @@ -155,10 +155,10 @@ netplay_input_state_t netplay_input_state_for(netplay_input_state_t *list, ret = (netplay_input_state_t)calloc(1, sizeof(struct netplay_input_state) + (size-1) * sizeof(uint32_t)); if (!ret) return NULL; - *list = ret; + *list = ret; ret->client_num = client_num; - ret->used = true; - ret->size = size; + ret->used = true; + ret->size = (uint32_t)size; return ret; } diff --git a/network/netplay/netplay_discovery.c b/network/netplay/netplay_discovery.c index 33118802b1..372a818b3d 100644 --- a/network/netplay/netplay_discovery.c +++ b/network/netplay/netplay_discovery.c @@ -168,7 +168,7 @@ bool netplay_discovery_driver_ctl(enum rarch_netplay_discovery_ctl_state state, NETPLAY_HOST_STR_LEN); /* And send it off */ - ret = sendto(lan_ad_client_fd, (const char *) &ad_packet_buffer, + ret = (int)sendto(lan_ad_client_fd, (const char *) &ad_packet_buffer, sizeof(struct ad_packet), 0, addr->ai_addr, addr->ai_addrlen); if (ret < (ssize_t) (2*sizeof(uint32_t))) RARCH_WARN("[discovery] Failed to send netplay discovery query (error: %d)\n", errno); @@ -262,8 +262,7 @@ bool netplay_lan_ad_server(netplay_t *netplay) /* Somebody queried, so check that it's valid */ addr_size = sizeof(their_addr); - - ret = recvfrom(lan_ad_server_fd, (char*)&ad_packet_buffer, + ret = (int)recvfrom(lan_ad_server_fd, (char*)&ad_packet_buffer, sizeof(struct ad_packet), 0, &their_addr, &addr_size); if (ret >= (ssize_t) (2 * sizeof(uint32_t))) { diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c index a005a963fc..021b51d35c 100644 --- a/network/netplay/netplay_handshake.c +++ b/network/netplay/netplay_handshake.c @@ -564,8 +564,8 @@ bool netplay_handshake_sync(netplay_t *netplay, autosave_unlock(); /* Send basic sync info */ - cmd[0] = htonl(NETPLAY_CMD_SYNC); - cmd[1] = htonl(2*sizeof(uint32_t) + cmd[0] = htonl(NETPLAY_CMD_SYNC); + cmd[1] = htonl(2*sizeof(uint32_t) /* Controller devices */ + MAX_INPUT_DEVICES*sizeof(uint32_t) @@ -580,11 +580,13 @@ bool netplay_handshake_sync(netplay_t *netplay, /* And finally, sram */ + mem_info.size); - cmd[2] = htonl(netplay->self_frame_count); - client_num = connection - netplay->connections + 1; + cmd[2] = htonl(netplay->self_frame_count); + client_num = (uint32_t)(connection - netplay->connections + 1); + if (netplay->local_paused || netplay->remote_paused) client_num |= NETPLAY_CMD_SYNC_BIT_PAUSED; - cmd[3] = htonl(client_num); + + cmd[3] = htonl(client_num); if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd, sizeof(cmd))) diff --git a/network/netplay/netplay_io.c b/network/netplay/netplay_io.c index d87cc1459a..4028515719 100644 --- a/network/netplay/netplay_io.c +++ b/network/netplay/netplay_io.c @@ -142,7 +142,7 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) } else { - uint32_t client_num = connection - netplay->connections + 1; + uint32_t client_num = (uint32_t)(connection - netplay->connections + 1); /* Mark the player for removal */ if (connection->mode == NETPLAY_CONNECTION_PLAYING || @@ -177,22 +177,23 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) */ void netplay_delayed_state_change(netplay_t *netplay) { - struct netplay_connection *connection; - size_t i; + unsigned i; for (i = 0; i < netplay->connections_size; i++) { - uint32_t client_num = i+1; - connection = &netplay->connections[i]; + uint32_t client_num = (uint32_t)(i + 1); + struct netplay_connection *connection = &netplay->connections[i]; + if ((connection->active || connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) && connection->delay_frame && connection->delay_frame <= netplay->self_frame_count) { /* Something was delayed! Prepare the MODE command */ uint32_t payload[15] = {0}; - payload[0] = htonl(connection->delay_frame); - payload[1] = htonl(client_num); - payload[2] = htonl(0); + payload[0] = htonl(connection->delay_frame); + payload[1] = htonl(client_num); + payload[2] = htonl(0); + memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); strncpy((char *) (payload + 7), connection->nick, NETPLAY_NICK_LEN); @@ -294,7 +295,7 @@ bool netplay_send_cur_input(netplay_t *netplay, if (netplay->is_server) { - to_client = connection - netplay->connections + 1; + to_client = (uint32_t)(connection - netplay->connections + 1); /* Send the other players' input data (FIXME: This involves an * unacceptable amount of recalculating) */ @@ -302,6 +303,7 @@ bool netplay_send_cur_input(netplay_t *netplay, { if (from_client == to_client) continue; + if ((netplay->connected_players & (1<have_real[from_client]) @@ -957,7 +959,7 @@ static bool netplay_get_cmd(netplay_t *netplay, RARCH_ERR("Netplay input from non-participating player.\n"); return netplay_cmd_nak(netplay, connection); } - client_num = connection - netplay->connections + 1; + client_num = (uint32_t)(connection - netplay->connections + 1); } if (client_num > MAX_CLIENTS) @@ -1129,7 +1131,7 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } - client_num = connection - netplay->connections + 1; + client_num = (uint32_t)(connection - netplay->connections + 1); handle_play_spectate(netplay, client_num, connection, cmd, 0, NULL); break; @@ -1180,7 +1182,7 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } - client_num = connection - netplay->connections + 1; + client_num = (unsigned)(connection - netplay->connections + 1); handle_play_spectate(netplay, client_num, connection, cmd, cmd_size, payload); break; @@ -1552,12 +1554,12 @@ static bool netplay_get_cmd(netplay_t *netplay, uint32_t frame; uint32_t isize; uint32_t rd, wn; - uint32_t client, client_num; + uint32_t client; uint32_t load_frame_count; size_t load_ptr; - struct compression_transcoder *ctrans; - - client_num = connection - netplay->connections + 1; + struct compression_transcoder *ctrans = NULL; + uint32_t client_num = (uint32_t) + (connection - netplay->connections + 1); /* Make sure we're ready for it */ if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) @@ -1926,8 +1928,8 @@ void netplay_handle_slaves(netplay_t *netplay) if (connection->active && connection->mode == NETPLAY_CONNECTION_SLAVE) { - uint32_t client_num = i + 1; uint32_t devices, device; + uint32_t client_num = (uint32_t)(i + 1); /* This is a slave connection. First, should we do anything at all? If * we've already "read" this data, then we can just ignore it */ diff --git a/network/netplay/netplay_sync.c b/network/netplay/netplay_sync.c index 69386916c5..18d0650c92 100644 --- a/network/netplay/netplay_sync.c +++ b/network/netplay/netplay_sync.c @@ -947,12 +947,14 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) * clients ahead of us stall */ for (i = 0; i < netplay->connections_size; i++) { - struct netplay_connection *connection = &netplay->connections[i]; uint32_t client_num; + struct netplay_connection *connection = &netplay->connections[i]; + if (!connection->active || connection->mode != NETPLAY_CONNECTION_PLAYING) continue; - client_num = i + 1; + + client_num = (uint32_t)(i + 1); /* Are they ahead? */ if (netplay->self_frame_count + 3 < netplay->read_frame_count[client_num]) diff --git a/runahead/run_ahead.c b/runahead/run_ahead.c index f59ddde051..b5e84de396 100644 --- a/runahead/run_ahead.c +++ b/runahead/run_ahead.c @@ -404,7 +404,7 @@ static bool runahead_load_state_secondary(void) set_fast_savestate(); okay = secondary_core_deserialize( - serialize_info->data_const, serialize_info->size); + serialize_info->data_const, (int)serialize_info->size); unset_fast_savestate(); if (!okay) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index f2f914edf0..d64e123136 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -146,7 +146,7 @@ bool write_file_with_random_name(char **tempDllPath, unsigned int number_value= (unsigned int)time_value; int number = 0; char *ext = strcpy_alloc_force(path_get_extension(*tempDllPath)); - int ext_len = strlen(ext); + int ext_len = (int)strlen(ext); if (ext_len > 0) { @@ -259,7 +259,7 @@ bool secondary_core_create(void) { device = port_map[port]; if (device >= 0) - secondary_core.retro_set_controller_port_device(port, device); + secondary_core.retro_set_controller_port_device((unsigned)port, (unsigned)device); } clear_controller_port_map(); } @@ -335,9 +335,9 @@ void secondary_core_destroy(void) void remember_controller_port_device(long port, long device) { if (port >= 0 && port < 16) - port_map[port] = device; + port_map[port] = (int)device; if (secondary_module && secondary_core.retro_set_controller_port_device) - secondary_core.retro_set_controller_port_device(port, device); + secondary_core.retro_set_controller_port_device((unsigned)port, (unsigned)device); } void clear_controller_port_map(void) diff --git a/tasks/task_audio_mixer.c b/tasks/task_audio_mixer.c index f0b95dfadb..4acc62f3f6 100644 --- a/tasks/task_audio_mixer.c +++ b/tasks/task_audio_mixer.c @@ -73,7 +73,7 @@ static int cb_nbio_audio_mixer_load(void *data, size_t len) image->buffer = buffer; image->buffer->buf = ptr; - image->buffer->bufsize = len; + image->buffer->bufsize = (unsigned)len; image->copy_data_over = true; nbio->is_finished = true; diff --git a/tasks/task_database.c b/tasks/task_database.c index d1cda3293f..9bb7033cf4 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -225,7 +225,7 @@ static bool intfstream_file_get_serial(const char *name, if (offset != 0 || size < (size_t) file_size) { - if (intfstream_seek(fd, offset, SEEK_SET) == -1) + if (intfstream_seek(fd, (int)offset, SEEK_SET) == -1) goto error; data = (uint8_t*)malloc(size); @@ -377,7 +377,7 @@ static bool intfstream_file_get_crc(const char *name, if (offset != 0 || size < (size_t) file_size) { - if (intfstream_seek(fd, offset, SEEK_SET) == -1) + if (intfstream_seek(fd, (int)offset, SEEK_SET) == -1) goto error; data = (uint8_t*)malloc(size); diff --git a/verbosity.c b/verbosity.c index ddb46512a9..1465b59a4c 100644 --- a/verbosity.c +++ b/verbosity.c @@ -121,17 +121,12 @@ void retro_main_log_file_deinit(void) #if !defined(HAVE_LOGGER) void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap) { -#if TARGET_OS_IPHONE - static int asl_initialized = 0; -#if !TARGET_IPHONE_SIMULATOR - static aslclient asl_client; -#endif -#endif - #if TARGET_OS_IPHONE #if TARGET_IPHONE_SIMULATOR vprintf(fmt, ap); #else + static aslclient asl_client;ß + static int asl_initialized = 0; if (!asl_initialized) { asl_client = asl_open( From 0fb766b9219f2a436ce3ef0c2f3d93ec978dbeb1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 16:15:31 +0200 Subject: [PATCH 205/517] Move variables --- input/input_driver.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 51a7b04877..2f5255285b 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -856,8 +856,6 @@ void input_menu_keys_pressed(void *data, input_bits_t *p_new_state) rarch_joypad_info_t joypad_info; const struct retro_keybind *binds[MAX_USERS] = {NULL}; settings_t *settings = (settings_t*)data; - const struct retro_keybind *binds_norm = NULL; - const struct retro_keybind *binds_auto = NULL; uint8_t max_users = (uint8_t)input_driver_max_users; uint8_t port_max = settings->bools.input_all_users_control_menu @@ -883,8 +881,8 @@ void input_menu_keys_pressed(void *data, input_bits_t *p_new_state) for (port = 0; port < port_max; port++) { - binds_norm = &input_config_binds[port][RARCH_ENABLE_HOTKEY]; - binds_auto = &input_autoconf_binds[port][RARCH_ENABLE_HOTKEY]; + const struct retro_keybind *binds_norm = &input_config_binds[port][RARCH_ENABLE_HOTKEY]; + const struct retro_keybind *binds_auto = &input_autoconf_binds[port][RARCH_ENABLE_HOTKEY]; if (check_input_driver_block_hotkey(binds_norm, binds_auto)) { From 88e7c03e1b04ce49f1b2802001f5a63cf38cfc38 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 16:27:37 +0200 Subject: [PATCH 206/517] filebrowser_parse - cleanups --- menu/widgets/menu_filebrowser.c | 35 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/menu/widgets/menu_filebrowser.c b/menu/widgets/menu_filebrowser.c index ac1edb47f2..86c943adcb 100644 --- a/menu/widgets/menu_filebrowser.c +++ b/menu/widgets/menu_filebrowser.c @@ -66,7 +66,6 @@ void filebrowser_parse(void *data, unsigned type_data) unsigned dirs_count = 0; settings_t *settings = config_get_ptr(); rarch_system_info_t *system = runloop_get_system_info(); - const struct retro_subsystem_info* subsystem = NULL; menu_displaylist_info_t *info = (menu_displaylist_info_t*)data; enum menu_displaylist_ctl_state type = (enum menu_displaylist_ctl_state) type_data; @@ -86,29 +85,31 @@ void filebrowser_parse(void *data, unsigned type_data) str_list = file_archive_get_file_list(path, info->exts); else { - subsystem = system->subsystem.data + content_get_subsystem(); - str_list = file_archive_get_file_list(path, subsystem->roms[content_get_subsystem_rom_id()].valid_extensions); + const struct retro_subsystem_info *subsystem = system->subsystem.data + content_get_subsystem(); + + if (subsystem) + str_list = file_archive_get_file_list(path, subsystem->roms[content_get_subsystem_rom_id()].valid_extensions); } } - else if (!string_is_empty(path) && filebrowser_types != FILEBROWSER_SELECT_FILE_SUBSYSTEM) + else if (!string_is_empty(path)) { - str_list = dir_list_new(path, - (filter_ext && info) ? info->exts : NULL, - true, settings->bools.show_hidden_files, true, false); - } - else if (!string_is_empty(path) && filebrowser_types == FILEBROWSER_SELECT_FILE_SUBSYSTEM) - { - subsystem = system->subsystem.data + content_get_subsystem(); - - if (subsystem && content_get_subsystem_rom_id() < subsystem->num_roms) + if (filebrowser_types == FILEBROWSER_SELECT_FILE_SUBSYSTEM) { - str_list = dir_list_new(path, - (filter_ext && info) ? subsystem->roms[content_get_subsystem_rom_id()].valid_extensions : NULL, - true, settings->bools.show_hidden_files, true, false); - } + const struct retro_subsystem_info *subsystem = + system->subsystem.data + content_get_subsystem(); + if (subsystem && content_get_subsystem_rom_id() < subsystem->num_roms) + str_list = dir_list_new(path, + (filter_ext && info) ? subsystem->roms[content_get_subsystem_rom_id()].valid_extensions : NULL, + true, settings->bools.show_hidden_files, true, false); + } + else + str_list = dir_list_new(path, + (filter_ext && info) ? info->exts : NULL, + true, settings->bools.show_hidden_files, true, false); } + switch (filebrowser_types) { case FILEBROWSER_SCAN_DIR: From e82882a1ab9de1098ace4ab0adc71b029f4808ea Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 16:33:46 +0200 Subject: [PATCH 207/517] Silence some Clang static analysis warnings --- menu/menu_entries.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/menu/menu_entries.c b/menu/menu_entries.c index ff893aad4d..05d4b69707 100644 --- a/menu/menu_entries.c +++ b/menu/menu_entries.c @@ -62,29 +62,40 @@ static void menu_list_free_list(file_list_t *list) static void menu_list_free(menu_list_t *menu_list) { - unsigned i; if (!menu_list) return; - for (i = 0; i < menu_list->menu_stack_size; i++) + if (menu_list->menu_stack) { - if (!menu_list->menu_stack[i]) - continue; + unsigned i; - menu_list_free_list(menu_list->menu_stack[i]); - menu_list->menu_stack[i] = NULL; - } - for (i = 0; i < menu_list->selection_buf_size; i++) - { - if (!menu_list->selection_buf[i]) - continue; + for (i = 0; i < menu_list->menu_stack_size; i++) + { + if (!menu_list->menu_stack[i]) + continue; - menu_list_free_list(menu_list->selection_buf[i]); - menu_list->selection_buf[i] = NULL; + menu_list_free_list(menu_list->menu_stack[i]); + menu_list->menu_stack[i] = NULL; + } + + free(menu_list->menu_stack); } - free(menu_list->menu_stack); - free(menu_list->selection_buf); + if (menu_list->selection_buf) + { + unsigned i; + + for (i = 0; i < menu_list->selection_buf_size; i++) + { + if (!menu_list->selection_buf[i]) + continue; + + menu_list_free_list(menu_list->selection_buf[i]); + menu_list->selection_buf[i] = NULL; + } + + free(menu_list->selection_buf); + } free(menu_list); } From f071e8b44063307052650a0952e817bd6bdc57fe Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 16:42:12 +0200 Subject: [PATCH 208/517] Solve more Clang static analysis warnings --- gfx/video_driver.c | 1 + menu/cbs/menu_cbs_deferred_push.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 30fc5756c5..3c796f1659 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -1012,6 +1012,7 @@ static bool video_driver_init_internal(bool *video_is_threaded) video.rgb32 = video_driver_state_filter ? video_driver_state_out_rgb32 : (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888); + video.parent = 0; /* Reset video frame count */ video_driver_frame_count = 0; diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index 6d29ea73d7..648caf0128 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -202,12 +202,15 @@ static int deferred_push_cursor_manager_list_deferred( if (!string_is_empty(info->path_b)) free(info->path_b); + if (!string_is_empty(info->path_c)) free(info->path_c); + + info->path_b = strdup(info->path); + if (!string_is_empty(info->path)) free(info->path); - info->path_b = strdup(info->path); info->path_c = strdup(query); info->path = strdup(rdb_path); From 50edd0d467aaaf22d180eb213531d29322083627 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 16:45:44 +0200 Subject: [PATCH 209/517] Remove unused variable --- gfx/drivers/xvideo.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gfx/drivers/xvideo.c b/gfx/drivers/xvideo.c index 0479fa73bb..94e81e6f87 100644 --- a/gfx/drivers/xvideo.c +++ b/gfx/drivers/xvideo.c @@ -423,7 +423,6 @@ static void *xv_init(const video_info_t *video, unsigned i; int ret; XWindowAttributes target; - char buf[128] = {0}; char title[128] = {0}; XSetWindowAttributes attributes = {0}; XVisualInfo visualtemplate = {0}; From f00a75ec8475b712fc404fedf607384ff35f655f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 17:02:44 +0200 Subject: [PATCH 210/517] Get rid of some spurious code in XMB - catched by Clang --- menu/drivers/xmb.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index cd8eca370a..aebc31bcd5 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3265,11 +3265,6 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) (xmb->icon_size * (!(xmb->depth == 1)? 2.1 : 1))) / left_thumb_height); } - else - { - left_thumb_width = left_thumb_width; - left_thumb_height = left_thumb_height; - } xmb_draw_thumbnail(video_info, xmb, &coord_white[0], width, height, From 9381d27f1f33c483777ad93f9ce541f33ac18b01 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 17:07:43 +0200 Subject: [PATCH 211/517] (miniupnpc) Get rid of warning - value computed is not used --- deps/miniupnpc/minissdpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/miniupnpc/minissdpc.c b/deps/miniupnpc/minissdpc.c index c2a00319ef..a6544787ec 100644 --- a/deps/miniupnpc/minissdpc.c +++ b/deps/miniupnpc/minissdpc.c @@ -584,7 +584,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[], struct in_addr mc_if; memset(&mc_if, 0, sizeof(mc_if)); mc_if.s_addr = pIPAddrTable->table[i].dwAddr; - setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0; + setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)); ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr; #ifndef DEBUG break; From 8e7b1ede7f09fd60a66416c3b6b69f22590140f6 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 17:35:27 +0200 Subject: [PATCH 212/517] Silence Clang warnings --- Makefile | 2 +- frontend/drivers/platform_win32.c | 14 +- gfx/common/win32_common.c | 3 +- network/netplay/netplay_sync.c | 212 +++++++++++++++++++----------- 4 files changed, 143 insertions(+), 88 deletions(-) diff --git a/Makefile b/Makefile index a9e72cb4b7..f51031259d 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ endif include Makefile.common ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang"),1) - DEFINES += -Wno-invalid-source-encoding + DEFINES += -Wno-invalid-source-encoding -Wno-incompatible-ms-struct endif ifeq ($(shell $(CC) -v 2>&1 | grep -c "tcc"),1) diff --git a/frontend/drivers/platform_win32.c b/frontend/drivers/platform_win32.c index cce85bce6e..8308446f83 100644 --- a/frontend/drivers/platform_win32.c +++ b/frontend/drivers/platform_win32.c @@ -148,15 +148,15 @@ static void gfx_set_dwm(void) static void frontend_win32_get_os(char *s, size_t len, int *major, int *minor) { - char buildStr[11] = {0}; - bool server = false; - const char *arch = ""; - bool serverR2 = false; + char buildStr[11] = {0}; + bool server = false; + const char *arch = ""; + bool serverR2 = false; #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 /* Windows 2000 and later */ - SYSTEM_INFO si = {0}; - OSVERSIONINFOEX vi = {0}; + SYSTEM_INFO si = {{0}}; + OSVERSIONINFOEX vi = {0}; vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); GetSystemInfo(&si); @@ -359,7 +359,7 @@ enum frontend_architecture frontend_win32_get_architecture(void) { #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 /* Windows 2000 and later */ - SYSTEM_INFO si = {0}; + SYSTEM_INFO si = {{0}}; GetSystemInfo(&si); diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index b4feaf53e4..4373168740 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -948,8 +948,9 @@ bool win32_suppress_screensaver(void *data, bool enable) #ifndef _XBOX if(enable) { - int major, minor; char tmp[PATH_MAX_LENGTH]; + int major = 0; + int minor = 0; const frontend_ctx_driver_t *frontend = frontend_get_ptr(); if (!frontend) diff --git a/network/netplay/netplay_sync.c b/network/netplay/netplay_sync.c index 18d0650c92..13eafacb29 100644 --- a/network/netplay/netplay_sync.c +++ b/network/netplay/netplay_sync.c @@ -126,35 +126,51 @@ static void netplay_merge_digital(netplay_t *netplay, { netplay_input_state_t simstate; uint32_t word, bit, client; - uint8_t share_mode = netplay->device_share_modes[device] & NETPLAY_SHARE_DIGITAL_BITS; + uint8_t share_mode = netplay->device_share_modes[device] + & NETPLAY_SHARE_DIGITAL_BITS; /* Make sure all real clients are accounted for */ - for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) + for (simstate = simframe->real_input[device]; + simstate; simstate = simstate->next) { - if (!simstate->used || simstate->size != resstate->size) continue; + if (!simstate->used || simstate->size != resstate->size) + continue; clients |= 1<client_num; } if (share_mode == NETPLAY_SHARE_DIGITAL_VOTE) { + unsigned i, j; + /* This just assumes we have no more than + * three words, will need to be adjusted for new devices */ + struct vote_count votes[3]; /* Vote mode requires counting all the bits */ - uint32_t client_count = 0; + uint32_t client_count = 0; + + for (i = 0; i < 3; i++) + for (j = 0; j < 32; j++) + votes[i].votes[j] = 0; - /* This just assumes we have no more than three words, will need to be adjusted for new devices */ - struct vote_count votes[3] = {0}; for (client = 0; client < MAX_CLIENTS; client++) { - if (!(clients & (1<size; word++) { - if (!digital[word]) continue; + if (!digital[word]) + continue; for (bit = 0; bit < 32; bit++) { - if (!(digital[word] & (1<data[word] & (1<data[word] |= (1<size; word++) { uint32_t part; - if (!digital[word]) continue; + if (!digital[word]) + continue; part = simstate->data[word]; + if (digital[word] == (uint32_t) -1) { /* Combine the whole word */ switch (share_mode) { - case NETPLAY_SHARE_DIGITAL_XOR: resstate->data[word] ^= part; break; - default: resstate->data[word] |= part; + case NETPLAY_SHARE_DIGITAL_XOR: + resstate->data[word] ^= part; + break; + default: + resstate->data[word] |= part; } } @@ -199,11 +223,15 @@ static void netplay_merge_digital(netplay_t *netplay, { for (bit = 0; bit < 32; bit++) { - if (!(digital[word] & (1<data[word] ^= part & (1<data[word] |= part & (1<data[word] ^= part & (1<data[word] |= part & (1<device_share_modes[device] & NETPLAY_SHARE_ANALOG_BITS; + uint8_t share_mode = netplay->device_share_modes[device] + & NETPLAY_SHARE_ANALOG_BITS; int32_t value = 0, new_value; /* Make sure all real clients are accounted for */ for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) { - if (!simstate->used || simstate->size != resstate->size) continue; + if (!simstate->used || simstate->size != resstate->size) + continue; clients |= 1<client_num; } for (client = 0; client < MAX_CLIENTS; client++) { - if (!(clients & (1<data[word]>>bit) & 0xFFFF); switch (share_mode) @@ -307,13 +340,13 @@ static void netplay_merge_analog(netplay_t *netplay, bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) { size_t prev; - struct delta_frame *simframe, *pframe; - netplay_input_state_t simstate, client_state = NULL, resstate, oldresstate, pstate; - uint32_t clients, client, client_count; uint32_t device; - bool ret = false; - - simframe = &netplay->buffer[sim_ptr]; + uint32_t clients, client, client_count; + netplay_input_state_t simstate, client_state = NULL, + resstate, oldresstate, pstate; + bool ret = false; + struct delta_frame *pframe = NULL; + struct delta_frame *simframe = &netplay->buffer[sim_ptr]; for (device = 0; device < MAX_INPUT_DEVICES; device++) { @@ -325,19 +358,23 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) /* Make sure all real clients are accounted for */ for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) { - if (!simstate->used || simstate->size != dsize) continue; + if (!simstate->used || simstate->size != dsize) + continue; clients |= 1<client_num; } for (client = 0; client < MAX_CLIENTS; client++) { - if (!(clients & (1<real_input[device], client, dsize, false, true); + simstate = netplay_input_state_for( + &simframe->real_input[device], client, dsize, false, true); if (!simstate) { - /* Don't already have this input, so must simulate if we're supposed to have it at all */ + /* Don't already have this input, so must + * simulate if we're supposed to have it at all */ if (netplay->read_frame_count[client] > simframe->frame) continue; simstate = netplay_input_state_for(&simframe->simlated_input[device], client, dsize, false, false); @@ -368,37 +405,39 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) * wavefronts. */ const uint32_t keep = - (1U<data[0] &= keep; simstate->data[0] |= pstate->data[0] & ~keep; } else - { memcpy(simstate->data, pstate->data, dsize * sizeof(uint32_t)); - } } client_state = simstate; client_count++; } - /* The frontend always uses the first resolved input, so make sure it's right */ + /* The frontend always uses the first resolved input, + * so make sure it's right */ while (simframe->resolved_input[device] && (simframe->resolved_input[device]->size != dsize || simframe->resolved_input[device]->client_num != 0)) { /* The default resolved input is of the wrong size! */ - netplay_input_state_t nextistate = simframe->resolved_input[device]->next; + netplay_input_state_t nextistate = + simframe->resolved_input[device]->next; free(simframe->resolved_input[device]); simframe->resolved_input[device] = nextistate; } - /* Now we copy the state, whether real or simulated, out into the resolved state */ - resstate = netplay_input_state_for(&simframe->resolved_input[device], 0, + /* Now we copy the state, whether real or simulated, + * out into the resolved state */ + resstate = netplay_input_state_for( + &simframe->resolved_input[device], 0, dsize, false, false); if (!resstate) continue; @@ -406,9 +445,11 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) if (client_count == 1) { /* Trivial in the common 1-client case */ - if (memcmp(resstate->data, client_state->data, dsize * sizeof(uint32_t))) + if (memcmp(resstate->data, client_state->data, + dsize * sizeof(uint32_t))) ret = true; - memcpy(resstate->data, client_state->data, dsize * sizeof(uint32_t)); + memcpy(resstate->data, client_state->data, + dsize * sizeof(uint32_t)); } else if (client_count == 0) @@ -426,23 +467,27 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) { /* Merge them */ /* Most devices have all the digital parts in the first word. */ - static const uint32_t digital_common[3] = {~0u, 0u, 0u}; + static const uint32_t digital_common[3] = {~0u, 0u, 0u}; static const uint32_t digital_keyboard[5] = {~0u, ~0u, ~0u, ~0u, ~0u}; const uint32_t *digital; if (dtype == RETRO_DEVICE_KEYBOARD) digital = digital_keyboard; else digital = digital_common; - oldresstate = netplay_input_state_for(&simframe->resolved_input[device], 1, dsize, false, false); + oldresstate = netplay_input_state_for( + &simframe->resolved_input[device], 1, dsize, false, false); if (!oldresstate) continue; memcpy(oldresstate->data, resstate->data, dsize * sizeof(uint32_t)); memset(resstate->data, 0, dsize * sizeof(uint32_t)); - netplay_merge_digital(netplay, resstate, simframe, device, clients, digital); - netplay_merge_analog(netplay, resstate, simframe, device, clients, dtype); + netplay_merge_digital(netplay, resstate, simframe, + device, clients, digital); + netplay_merge_analog(netplay, resstate, simframe, + device, clients, dtype); - if (memcmp(resstate->data, oldresstate->data, dsize * sizeof(uint32_t))) + if (memcmp(resstate->data, oldresstate->data, + dsize * sizeof(uint32_t))) ret = true; } @@ -451,7 +496,8 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) return ret; } -static void netplay_handle_frame_hash(netplay_t *netplay, struct delta_frame *delta) +static void netplay_handle_frame_hash(netplay_t *netplay, + struct delta_frame *delta) { if (netplay->is_server) { @@ -468,12 +514,10 @@ static void netplay_handle_frame_hash(netplay_t *netplay, struct delta_frame *de uint32_t local_crc = netplay_delta_frame_crc(netplay, delta); if (local_crc != delta->crc) { + /* If the very first check frame is wrong, + * they probably just don't work */ if (!netplay->crc_validity_checked) - { - /* If the very first check frame is wrong, they probably just don't - * work */ netplay->crcs_valid = false; - } else if (netplay->crcs_valid) { /* Fix this! */ @@ -483,15 +527,11 @@ static void netplay_handle_frame_hash(netplay_t *netplay, struct delta_frame *de RARCH_ERR("Netplay CRCs mismatch!\n"); } else - { netplay_cmd_request_savestate(netplay); - } } } else if (!netplay->crc_validity_checked) - { netplay->crc_validity_checked = true; - } } } @@ -505,29 +545,33 @@ bool netplay_sync_pre_frame(netplay_t *netplay) { retro_ctx_serialize_info_t serial_info; - if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->run_ptr], netplay->run_frame_count)) + if (netplay_delta_frame_ready(netplay, + &netplay->buffer[netplay->run_ptr], netplay->run_frame_count)) { serial_info.data_const = NULL; - serial_info.data = netplay->buffer[netplay->run_ptr].state; - serial_info.size = netplay->state_size; + serial_info.data = netplay->buffer[netplay->run_ptr].state; + serial_info.size = netplay->state_size; memset(serial_info.data, 0, serial_info.size); - if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) || netplay->run_frame_count == 0) + if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) + || netplay->run_frame_count == 0) { /* Don't serialize until it's safe */ } - else if (!(netplay->quirks & NETPLAY_QUIRK_NO_SAVESTATES) && core_serialize(&serial_info)) + else if (!(netplay->quirks & NETPLAY_QUIRK_NO_SAVESTATES) + && core_serialize(&serial_info)) { - if (netplay->force_send_savestate && !netplay->stall && !netplay->remote_paused) + if (netplay->force_send_savestate && !netplay->stall + && !netplay->remote_paused) { - /* Bring our running frame and input frames into parity so we don't - * send old info */ + /* Bring our running frame and input frames into + * parity so we don't send old info. */ if (netplay->run_ptr != netplay->self_ptr) { memcpy(netplay->buffer[netplay->self_ptr].state, netplay->buffer[netplay->run_ptr].state, netplay->state_size); - netplay->run_ptr = netplay->self_ptr; + netplay->run_ptr = netplay->self_ptr; netplay->run_frame_count = netplay->self_frame_count; } @@ -545,7 +589,8 @@ bool netplay_sync_pre_frame(netplay_t *netplay) netplay->stateless_mode = true; } - /* If we can't transmit savestates, we must stall until the client is ready */ + /* If we can't transmit savestates, we must stall + * until the client is ready. */ if (netplay->run_frame_count > 0 && (netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)) && (netplay->connections_size == 0 || !netplay->connections[0].active || @@ -566,11 +611,14 @@ bool netplay_sync_pre_frame(netplay_t *netplay) /* Check for a connection */ FD_ZERO(&fds); FD_SET(netplay->listen_fd, &fds); - if (socket_select(netplay->listen_fd + 1, &fds, NULL, NULL, &tmp_tv) > 0 && + if (socket_select(netplay->listen_fd + 1, + &fds, NULL, NULL, &tmp_tv) > 0 && FD_ISSET(netplay->listen_fd, &fds)) { addr_size = sizeof(their_addr); - new_fd = accept(netplay->listen_fd, (struct sockaddr*)&their_addr, &addr_size); + new_fd = accept(netplay->listen_fd, + (struct sockaddr*)&their_addr, &addr_size); + if (new_fd < 0) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); @@ -614,8 +662,10 @@ bool netplay_sync_pre_frame(netplay_t *netplay) { if (connection_num == 0) { - netplay->connections = (struct netplay_connection*)malloc(sizeof(struct netplay_connection)); - if (netplay->connections == NULL) + netplay->connections = (struct netplay_connection*) + malloc(sizeof(struct netplay_connection)); + + if (!netplay->connections) { socket_close(new_fd); goto process; @@ -626,10 +676,13 @@ bool netplay_sync_pre_frame(netplay_t *netplay) else { size_t new_connections_size = netplay->connections_size * 2; - struct netplay_connection *new_connections = (struct netplay_connection*) + struct netplay_connection + *new_connections = (struct netplay_connection*) + realloc(netplay->connections, new_connections_size*sizeof(struct netplay_connection)); - if (new_connections == NULL) + + if (!new_connections) { socket_close(new_fd); goto process; @@ -694,7 +747,8 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) /* We've finished an input frame even if we're stalling */ if ((!stalled || netplay->stall == NETPLAY_STALL_INPUT_LATENCY) && - netplay->self_frame_count < netplay->run_frame_count + netplay->input_latency_frames) + netplay->self_frame_count < + netplay->run_frame_count + netplay->input_latency_frames) { netplay->self_ptr = NEXT_PTR(netplay->self_ptr); netplay->self_frame_count++; From ff9aebedcae2b8a74f42fe35120c0f0a989158fa Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 17:48:11 +0200 Subject: [PATCH 213/517] Some more Clang warning fixes --- gfx/common/d3d12_common.c | 41 ++++++++++++++++++---------------- network/netplay/netplay_sync.c | 4 ++-- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/gfx/common/d3d12_common.c b/gfx/common/d3d12_common.c index 163fd676f5..169e1d6ce4 100644 --- a/gfx/common/d3d12_common.c +++ b/gfx/common/d3d12_common.c @@ -206,36 +206,39 @@ bool d3d12_init_queue(d3d12_video_t* d3d12) return true; } -bool d3d12_init_swapchain(d3d12_video_t* d3d12, int width, int height, HWND hwnd) +bool d3d12_init_swapchain(d3d12_video_t* d3d12, + int width, int height, HWND hwnd) { - { - DXGI_SWAP_CHAIN_DESC desc = { 0 }; - desc.BufferCount = countof(d3d12->chain.renderTargets); - desc.BufferDesc.Width = width; - desc.BufferDesc.Height = height; - desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.SampleDesc.Count = 1; + unsigned i; + DXGI_SWAP_CHAIN_DESC desc; + + memset(&desc, 0, sizeof(DXGI_SWAP_CHAIN_DESC)); + + desc.BufferCount = countof(d3d12->chain.renderTargets); + desc.BufferDesc.Width = width; + desc.BufferDesc.Height = height; + desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; #if 0 - desc.BufferDesc.RefreshRate.Numerator = 60; - desc.BufferDesc.RefreshRate.Denominator = 1; - desc.SampleDesc.Quality = 0; + desc.BufferDesc.RefreshRate.Numerator = 60; + desc.BufferDesc.RefreshRate.Denominator = 1; + desc.SampleDesc.Quality = 0; #endif - desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.OutputWindow = hwnd; - desc.Windowed = TRUE; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.OutputWindow = hwnd; + desc.Windowed = TRUE; #if 0 - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; #else - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; #endif - DXGICreateSwapChain(d3d12->factory, d3d12->queue.handle, &desc, &d3d12->chain.handle); - } + DXGICreateSwapChain(d3d12->factory, d3d12->queue.handle, &desc, &d3d12->chain.handle); DXGIMakeWindowAssociation(d3d12->factory, hwnd, DXGI_MWA_NO_ALT_ENTER); d3d12->chain.frame_index = DXGIGetCurrentBackBufferIndex(d3d12->chain.handle); - for (int i = 0; i < countof(d3d12->chain.renderTargets); i++) + for (i = 0; i < countof(d3d12->chain.renderTargets); i++) { DXGIGetSwapChainBuffer(d3d12->chain.handle, i, &d3d12->chain.renderTargets[i]); D3D12CreateRenderTargetView( diff --git a/network/netplay/netplay_sync.c b/network/netplay/netplay_sync.c index 13eafacb29..7da8a893c1 100644 --- a/network/netplay/netplay_sync.c +++ b/network/netplay/netplay_sync.c @@ -352,8 +352,8 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) { unsigned dtype = netplay->config_devices[device]&RETRO_DEVICE_MASK; uint32_t dsize = netplay_expected_input_size(netplay, 1 << device); - clients = netplay->device_clients[device]; - client_count = 0; + clients = netplay->device_clients[device]; + client_count = 0; /* Make sure all real clients are accounted for */ for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) From 260cd6248233e26e450d24024d51b56821be2da6 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 18:53:26 +0200 Subject: [PATCH 214/517] Cleanup some CXX_BUILD warnings --- gfx/drivers/d3d11.c | 80 ++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 7bb3474fec..73730b8a4c 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -117,9 +117,10 @@ static void d3d11_overlay_set_alpha(void* data, unsigned index, float mod) static bool d3d11_overlay_load(void* data, const void* image_data, unsigned num_images) { + D3D11_BUFFER_DESC desc; + D3D11_MAPPED_SUBRESOURCE mapped_vbo; int i; d3d11_sprite_t* sprites; - D3D11_MAPPED_SUBRESOURCE mapped_vbo; d3d11_video_t* d3d11 = (d3d11_video_t*)data; const struct texture_image* images = (const struct texture_image*)image_data; @@ -128,19 +129,19 @@ static bool d3d11_overlay_load(void* data, const void* image_data, unsigned num_ d3d11_free_overlays(d3d11); d3d11->overlays.count = num_images; - d3d11->overlays.textures = (d3d11_texture_t*)calloc(num_images, sizeof(d3d11_texture_t)); + d3d11->overlays.textures = (d3d11_texture_t*)calloc( + num_images, sizeof(d3d11_texture_t)); - { - D3D11_BUFFER_DESC desc = { sizeof(d3d11_sprite_t) * num_images }; - desc.Usage = D3D11_USAGE_DYNAMIC; - - desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - D3D11CreateBuffer(d3d11->device, &desc, NULL, &d3d11->overlays.vbo); - } + desc.ByteWidth = sizeof(d3d11_sprite_t) * num_images; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + D3D11CreateBuffer(d3d11->device, &desc, NULL, &d3d11->overlays.vbo); D3D11MapBuffer(d3d11->context, d3d11->overlays.vbo, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_vbo); - sprites = (d3d11_sprite_t*)mapped_vbo.pData; + sprites = (d3d11_sprite_t*)mapped_vbo.pData; for (i = 0; i < num_images; i++) { @@ -152,26 +153,27 @@ static bool d3d11_overlay_load(void* data, const void* image_data, unsigned num_ d3d11_init_texture(d3d11->device, &d3d11->overlays.textures[i]); d3d11_update_texture( - d3d11->context, images[i].width, images[i].height, 0, DXGI_FORMAT_B8G8R8A8_UNORM, + d3d11->context, images[i].width, + images[i].height, 0, DXGI_FORMAT_B8G8R8A8_UNORM, images[i].pixels, &d3d11->overlays.textures[i]); - sprites[i].pos.x = 0.0f; - sprites[i].pos.y = 0.0f; - sprites[i].pos.w = 1.0f; - sprites[i].pos.h = 1.0f; + sprites[i].pos.x = 0.0f; + sprites[i].pos.y = 0.0f; + sprites[i].pos.w = 1.0f; + sprites[i].pos.h = 1.0f; - sprites[i].coords.u = 0.0f; - sprites[i].coords.v = 0.0f; - sprites[i].coords.w = 1.0f; - sprites[i].coords.h = 1.0f; + sprites[i].coords.u = 0.0f; + sprites[i].coords.v = 0.0f; + sprites[i].coords.w = 1.0f; + sprites[i].coords.h = 1.0f; sprites[i].params.scaling = 1; sprites[i].params.rotation = 0; - sprites[i].colors[0] = 0xFFFFFFFF; - sprites[i].colors[1] = sprites[i].colors[0]; - sprites[i].colors[2] = sprites[i].colors[0]; - sprites[i].colors[3] = sprites[i].colors[0]; + sprites[i].colors[0] = 0xFFFFFFFF; + sprites[i].colors[1] = sprites[i].colors[0]; + sprites[i].colors[2] = sprites[i].colors[0]; + sprites[i].colors[3] = sprites[i].colors[0]; } D3D11UnmapBuffer(d3d11->context, d3d11->overlays.vbo, 0); @@ -752,29 +754,31 @@ d3d11_gfx_init(const video_info_t* video, const input_driver_t** input, void** i d3d11_set_filtering(d3d11, 0, video->smooth); { + D3D11_BUFFER_DESC desc; d3d11_vertex_t vertices[] = { { { 0.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, { { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, { { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, { { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, }; + D3D11_SUBRESOURCE_DATA + vertexData = { vertices }; - { - D3D11_BUFFER_DESC desc = { 0 }; - desc.Usage = D3D11_USAGE_IMMUTABLE; - desc.ByteWidth = sizeof(vertices); - desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + desc.ByteWidth = sizeof(vertices); + desc.Usage = D3D11_USAGE_IMMUTABLE; + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; - D3D11_SUBRESOURCE_DATA vertexData = { vertices }; - D3D11CreateBuffer(d3d11->device, &desc, &vertexData, &d3d11->frame.vbo); - desc.Usage = D3D11_USAGE_DYNAMIC; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - D3D11CreateBuffer(d3d11->device, &desc, &vertexData, &d3d11->menu.vbo); + D3D11CreateBuffer(d3d11->device, &desc, &vertexData, &d3d11->frame.vbo); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + D3D11CreateBuffer(d3d11->device, &desc, &vertexData, &d3d11->menu.vbo); - d3d11->sprites.capacity = 4096; - desc.ByteWidth = sizeof(d3d11_sprite_t) * d3d11->sprites.capacity; - D3D11CreateBuffer(d3d11->device, &desc, NULL, &d3d11->sprites.vbo); - } + d3d11->sprites.capacity = 4096; + desc.ByteWidth = sizeof(d3d11_sprite_t) * d3d11->sprites.capacity; + D3D11CreateBuffer(d3d11->device, &desc, NULL, &d3d11->sprites.vbo); } { From afe77527cdf36f4d23a68c281e728264829d5ef6 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 18:57:39 +0200 Subject: [PATCH 215/517] Some cleanups for C89_BUILD --- ui/drivers/ui_win32.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ui/drivers/ui_win32.c b/ui/drivers/ui_win32.c index 4b5dfb21f6..8de635b29c 100644 --- a/ui/drivers/ui_win32.c +++ b/ui/drivers/ui_win32.c @@ -74,6 +74,7 @@ typedef struct ui_companion_win32 void *empty; } ui_companion_win32_t; +#ifdef HAVE_SHADERPIPELINE enum shader_param_ctrl_type { SHADER_PARAM_CTRL_NONE = 0, @@ -221,6 +222,7 @@ static void shader_dlg_params_clear(void) control->type = SHADER_PARAM_CTRL_NONE; } } +#endif void shader_dlg_params_reload(void) { @@ -329,6 +331,7 @@ void shader_dlg_params_reload(void) #endif } +#ifdef HAVE_SHADERPIPELINE static void shader_dlg_update_on_top_state(void) { bool on_top = SendMessage(g_shader_dlg.on_top_checkbox.hwnd, @@ -339,7 +342,7 @@ static void shader_dlg_update_on_top_state(void) SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } -void shader_dlg_show(HWND parent_hwnd) +static void shader_dlg_show(HWND parent_hwnd) { const ui_window_t *window = ui_companion_driver_get_window_ptr(); @@ -364,6 +367,7 @@ void shader_dlg_show(HWND parent_hwnd) window->set_focused(&g_shader_dlg.window); } +#endif #if 0 static LRESULT CALLBACK ShaderDlgWndProc(HWND hwnd, UINT message, @@ -687,11 +691,11 @@ LRESULT win32_menu_loop(HWND owner, WPARAM wparam) case ID_M_FULL_SCREEN: cmd = CMD_EVENT_FULLSCREEN_TOGGLE; break; -#ifndef _XBOX case ID_M_SHADER_PARAMETERS: +#if !defined(_XBOX) && defined(HAVE_SHADERPIPELINE) shader_dlg_show(owner); - break; #endif + break; case ID_M_MOUSE_GRAB: cmd = CMD_EVENT_GRAB_MOUSE_TOGGLE; break; From b6764c43db2c0427037e391e93aa514abbf429cd Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 19:09:00 +0200 Subject: [PATCH 216/517] Buildfix --- gfx/common/win32_common.h | 2 -- ui/drivers/ui_win32.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/gfx/common/win32_common.h b/gfx/common/win32_common.h index 963db7f2cf..d780dc5a67 100644 --- a/gfx/common/win32_common.h +++ b/gfx/common/win32_common.h @@ -62,8 +62,6 @@ void create_gdi_context(HWND hwnd, bool *quit); bool gdi_has_menu_frame(void); -bool win32_shader_dlg_init(void); -void shader_dlg_show(HWND parent_hwnd); void shader_dlg_params_reload(void); #endif diff --git a/ui/drivers/ui_win32.c b/ui/drivers/ui_win32.c index 8de635b29c..ec7c2265cc 100644 --- a/ui/drivers/ui_win32.c +++ b/ui/drivers/ui_win32.c @@ -447,7 +447,7 @@ static LRESULT CALLBACK ShaderDlgWndProc(HWND hwnd, UINT message, return DefWindowProc(hwnd, message, wparam, lparam); } -bool win32_shader_dlg_init(void) +static bool win32_shader_dlg_init(void) { static bool inited = false; int pos_y; From f3e0a627af2e040e85f2ac028317565f7f80e259 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 19:11:06 +0200 Subject: [PATCH 217/517] Get rid of one MSVC warning --- menu/drivers/xmb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index aebc31bcd5..ee8f467dc3 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -4867,7 +4867,7 @@ static int xmb_list_push(void *data, void *userdata, { menu_displaylist_ctx_parse_entry_t entry; int ret = -1; - int i = 0; + unsigned i = 0; core_info_list_t *list = NULL; menu_handle_t *menu = (menu_handle_t*)data; @@ -4970,7 +4970,7 @@ static int xmb_list_push(void *data, void *userdata, if (subsystem) { - for (i = 0; i < system->subsystem.size; i++, subsystem++) + for (i = 0; i < (unsigned)system->subsystem.size; i++, subsystem++) { char s[PATH_MAX_LENGTH]; if (content_get_subsystem() == i) From fdec9c50e07aa617f6a43a891cbd65452ed280c2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 19:26:49 +0200 Subject: [PATCH 218/517] (task_database.c) Add some RARCH_INTERNAL ifdefs --- tasks/task_database.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tasks/task_database.c b/tasks/task_database.c index 9bb7033cf4..eb8f53df05 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -34,7 +34,9 @@ #include "../list_special.h" #include "../msg_hash.h" #include "../playlist.h" +#ifdef RARCH_INTERNAL #include "../retroarch.h" +#endif #include "../verbosity.h" #include "../core_info.h" @@ -144,12 +146,13 @@ static int task_database_iterate_start(database_info_handle_t *db, name); if (!string_is_empty(msg)) + { +#ifdef RARCH_INTERNAL runloop_msg_queue_push(msg, 1, 180, true); - -#if 0 - RARCH_LOG("msg: %s\n", msg); +#else + RARCH_LOG("msg: %s\n", msg); #endif - + } db->status = DATABASE_STATUS_ITERATE; @@ -1191,14 +1194,16 @@ static void task_database_handler(retro_task_t *task) } else { + const char *msg = NULL; if (db->is_directory) - runloop_msg_queue_push( - msg_hash_to_str(MSG_SCANNING_OF_DIRECTORY_FINISHED), - 0, 180, true); + msg = msg_hash_to_str(MSG_SCANNING_OF_DIRECTORY_FINISHED); else - runloop_msg_queue_push( - msg_hash_to_str(MSG_SCANNING_OF_FILE_FINISHED), - 0, 180, true); + msg = msg_hash_to_str(MSG_SCANNING_OF_FILE_FINISHED); +#ifdef RARCH_INTERNAL + runloop_msg_queue_push(msg, 0, 180, true); +#else + RARCH_LOG("msg: %s\n", msg); +#endif goto task_finished; } break; From 22e5d000c25622ed755431a094e35f058e070234 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 19:30:36 +0200 Subject: [PATCH 219/517] Cleanups --- tasks/task_database_cue.c | 58 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/tasks/task_database_cue.c b/tasks/task_database_cue.c index 94db29ed40..fa53bcb264 100644 --- a/tasks/task_database_cue.c +++ b/tasks/task_database_cue.c @@ -36,7 +36,6 @@ #include "tasks_internal.h" -#include "../driver.h" #include "../list_special.h" #include "../msg_hash.h" #include "../verbosity.h" @@ -427,20 +426,20 @@ static bool update_cand(ssize_t *cand_index, ssize_t *last_index, size_t *largest, char *last_file, size_t *offset, size_t *size, char *track_path, size_t max_len) { - if (*cand_index != -1) - { - if ((unsigned)(*last_index - *cand_index) > *largest) - { - *largest = *last_index - *cand_index; - strlcpy(track_path, last_file, max_len); - *offset = *cand_index; - *size = *largest; + if (*cand_index != -1) + { + if ((unsigned)(*last_index - *cand_index) > *largest) + { + *largest = *last_index - *cand_index; + strlcpy(track_path, last_file, max_len); + *offset = *cand_index; + *size = *largest; + *cand_index = -1; + return true; + } *cand_index = -1; - return true; - } - *cand_index = -1; - } - return false; + } + return false; } int cue_find_track(const char *cue_path, bool first, @@ -488,17 +487,16 @@ int cue_find_track(const char *cue_path, bool first, if (string_is_equal(tmp_token, "FILE")) { /* Set last index to last EOF */ - if (file_size != -1) { + if (file_size != -1) last_index = file_size; - } /* We're changing files since the candidate, update it */ if (update_cand(&cand_index, &last_index, &largest, last_file, offset, - size, track_path, max_len)) { + size, track_path, max_len)) + { rv = 0; - if (first) { + if (first) goto clean; - } } get_token(fd, tmp_token, MAX_TOKEN_LEN); @@ -515,7 +513,9 @@ int cue_find_track(const char *cue_path, bool first, get_token(fd, tmp_token, MAX_TOKEN_LEN); is_data = !string_is_equal(tmp_token, "AUDIO"); ++track; - } else if (string_is_equal(tmp_token, "INDEX")) { + } + else if (string_is_equal(tmp_token, "INDEX")) + { int m, s, f; get_token(fd, tmp_token, MAX_TOKEN_LEN); get_token(fd, tmp_token, MAX_TOKEN_LEN); @@ -531,32 +531,30 @@ int cue_find_track(const char *cue_path, bool first, /* If we've changed tracks since the candidate, update it */ if (cand_track != -1 && track != cand_track && update_cand(&cand_index, &last_index, &largest, last_file, offset, - size, track_path, max_len)) { + size, track_path, max_len)) + { rv = 0; - if (first) { + if (first) goto clean; - } } - if (!is_data) { + if (!is_data) continue; - } - if (cand_index == -1) { + if (cand_index == -1) + { cand_index = last_index; cand_track = track; } } } - if (file_size != -1) { + if (file_size != -1) last_index = file_size; - } if (update_cand(&cand_index, &last_index, &largest, last_file, offset, - size, track_path, max_len)) { + size, track_path, max_len)) rv = 0; - } clean: free(cue_dir); From 908de15e7bfb0e2549b2ded0ac164dd627dd2531 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 22:12:59 +0200 Subject: [PATCH 220/517] Start adding samples - not done yet --- samples/tasks/database/Makefile | 125 ++++++++++++++++++++++++++++++++ tasks/tasks_internal.h | 1 - 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 samples/tasks/database/Makefile diff --git a/samples/tasks/database/Makefile b/samples/tasks/database/Makefile new file mode 100644 index 0000000000..f7741d9a4f --- /dev/null +++ b/samples/tasks/database/Makefile @@ -0,0 +1,125 @@ +compiler := gcc +extra_flags := +use_neon := 0 +release := release +EXE_EXT := +TARGET := database_task + +ifeq ($(platform),) +platform = unix +ifeq ($(shell uname -a),) + platform = win +else ifneq ($(findstring MINGW,$(shell uname -a)),) + platform = win +else ifneq ($(findstring Darwin,$(shell uname -a)),) + platform = osx + arch = intel +ifeq ($(shell uname -p),powerpc) + arch = ppc +endif +else ifneq ($(findstring win,$(shell uname -a)),) + platform = win +endif +endif + +ifeq ($(compiler),gcc) +extra_rules_gcc := $(shell $(compiler) -dumpmachine) +endif + +ifneq (,$(findstring armv7,$(extra_rules_gcc))) +extra_flags += -mcpu=cortex-a9 -mtune=cortex-a9 -mfpu=neon +use_neon := 1 +endif + +ifneq (,$(findstring hardfloat,$(extra_rules_gcc))) +extra_flags += -mfloat-abi=hard +endif + +ifeq (release,$(build)) +extra_flags += -O2 +endif + +ifeq (debug,$(build)) +extra_flags += -O0 -g +endif + +ldflags := + +EXE_EXT := +ifeq ($(platform), unix) +else ifeq ($(platform), osx) +compiler := $(CC) +else +EXE_EXT = .exe +endif + +CORE_DIR = ../../.. +LIBRETRO_COMM_DIR = $(CORE_DIR)/libretro-common + +CC := $(compiler) +CXX := $(subst CC,++,$(compiler)) +flags := -I$(LIBRETRO_COMM_DIR)/include +asflags := $(extra_flags) +LDFLAGS := +flags += -std=c99 +INCFLAGS := -I$(LIBRETRO_COMM_DIR)/include + +SOURCES_C := \ + $(CORE_DIR)/tasks/task_database.c \ + $(CORE_DIR)/tasks/task_database_cue.c \ + $(CORE_DIR)/database_info.c \ + $(CORE_DIR)/core_info.c \ + $(CORE_DIR)/file_path_str.c \ + $(CORE_DIR)/playlist.c \ + $(CORE_DIR)/verbosity.c \ + $(LIBRETRO_COMM_DIR)/file/archive_file.c \ + $(LIBRETRO_COMM_DIR)/file/config_file.c \ + $(LIBRETRO_COMM_DIR)/file/file_path.c \ + $(LIBRETRO_COMM_DIR)/file/retro_dirent.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_strl.c \ + $(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \ + $(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.c \ + $(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \ + $(LIBRETRO_COMM_DIR)/queues/task_queue.c \ + $(LIBRETRO_COMM_DIR)/lists/dir_list.c \ + $(LIBRETRO_COMM_DIR)/lists/string_list.c \ + $(LIBRETRO_COMM_DIR)/streams/interface_stream.c \ + $(LIBRETRO_COMM_DIR)/streams/memory_stream.c \ + $(LIBRETRO_COMM_DIR)/streams/file_stream.c \ + $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c + +#SOURCES_C += $(CORE_DIR)/msg_hash.c + +DEFINES = + +OBJECTS = $(SOURCES_C:.c=.o) + +OBJOUT = -o +LINKOUT = -o + +ifneq (,$(findstring msvc,$(platform))) + OBJOUT = -Fo +LINKOUT = -out: +ifeq ($(STATIC_LINKING),1) + LD ?= lib.exe +else + LD = link.exe +endif +else + LD = $(CC) +endif + +all: $(TARGET)$(EXE_EXT) +$(TARGET)$(EXE_EXT): $(OBJECTS) + $(LD) $(LINKOUT)$@ $(SHARED) $(OBJECTS) $(LDFLAGS) $(LIBS) + +%.o: %.c + $(CC) $(INCFLAGS) $(CFLAGS) -c $(OBJOUT)$@ $< + +%.o: %.cpp + $(CXX) $(INCFLAGS) $(CXXFLAGS) -c $(OBJOUT)$@ $< + +clean: + rm -f $(OBJECTS) diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index bbb11866ad..9b6c0ff9d2 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -32,7 +32,6 @@ #include "../content.h" #include "../core_type.h" #include "../msg_hash.h" -#include "../frontend/frontend_driver.h" RETRO_BEGIN_DECLS From 86c826350edcccb6e6767d118e88203fd6b99fcb Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 22:18:25 +0200 Subject: [PATCH 221/517] Start moving dependencies out of certain files --- core_info.c | 1 - database_info.c | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core_info.c b/core_info.c index 76b979046b..a2954b14c2 100644 --- a/core_info.c +++ b/core_info.c @@ -32,7 +32,6 @@ #include "config.def.h" #include "core_info.h" #include "configuration.h" -#include "file_path_special.h" #include "list_special.h" static const char *core_info_tmp_path = NULL; diff --git a/database_info.c b/database_info.c index 433bd275aa..007fce58c4 100644 --- a/database_info.c +++ b/database_info.c @@ -21,12 +21,13 @@ #include #include #include +#include #include #include "libretro-db/libretrodb.h" -#include "list_special.h" #include "database_info.h" +#include "list_special.h" #include "verbosity.h" int database_info_build_query_enum(char *s, size_t len, From 8b4b5e88ce22ebb718f9c7a007e2d91357e60715 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 9 Apr 2018 22:20:51 +0200 Subject: [PATCH 222/517] Update --- core_info.c | 1 + 1 file changed, 1 insertion(+) diff --git a/core_info.c b/core_info.c index a2954b14c2..76b979046b 100644 --- a/core_info.c +++ b/core_info.c @@ -32,6 +32,7 @@ #include "config.def.h" #include "core_info.h" #include "configuration.h" +#include "file_path_special.h" #include "list_special.h" static const char *core_info_tmp_path = NULL; From 867981b74b619a4892f97db071da4c75a5eb73a5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 00:07:37 +0200 Subject: [PATCH 223/517] Include this header include --- ctr/ctr_system.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ctr/ctr_system.c b/ctr/ctr_system.c index 29594251e7..9126ba009e 100644 --- a/ctr/ctr_system.c +++ b/ctr/ctr_system.c @@ -1,5 +1,6 @@ #include <3ds.h> #include +#include <3ds/services/apt.h> #include #include #include From 445f176e242dde89ecc235873f32a3264d60ad2a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 00:14:23 +0200 Subject: [PATCH 224/517] (Xbox) Enable HAVE_RUNAHEAD for Xbox OG/360 --- pkg/msvc/RetroArch-360/RetroArch-360.vcxproj | 12 ++++++------ pkg/msvc/RetroArch-Xbox1/RetroArch-Xbox1.vcproj | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/msvc/RetroArch-360/RetroArch-360.vcxproj b/pkg/msvc/RetroArch-360/RetroArch-360.vcxproj index d297901003..83399e9ae9 100644 --- a/pkg/msvc/RetroArch-360/RetroArch-360.vcxproj +++ b/pkg/msvc/RetroArch-360/RetroArch-360.vcxproj @@ -113,7 +113,7 @@ true false MultiThreadedDebug - _DEBUG;_XBOX;HAVE_XINPUT2;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_NETWORKING;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + _DEBUG;_XBOX;HAVE_XINPUT2;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_NETWORKING;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN Callcap $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -152,7 +152,7 @@ AnalyzeOnly false MultiThreadedDebug - _DEBUG;_XBOX;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + _DEBUG;_XBOX;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN Callcap $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -192,7 +192,7 @@ Size false MultiThreaded - NDEBUG;_XBOX;PROFILE;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + NDEBUG;_XBOX;PROFILE;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN Callcap $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -237,7 +237,7 @@ Size false MultiThreaded - NDEBUG;_XBOX;PROFILE;FASTCAP;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XUI;HAVE_MENU;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + NDEBUG;_XBOX;PROFILE;FASTCAP;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XUI;HAVE_MENU;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -279,7 +279,7 @@ false false MultiThreaded - NDEBUG;_XBOX;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE=1;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XUI;HAVE_MENU;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + NDEBUG;_XBOX;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE=1;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XUI;HAVE_MENU;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -321,7 +321,7 @@ false false MultiThreaded - NDEBUG;_XBOX;LTCG;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + NDEBUG;_XBOX;LTCG;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) diff --git a/pkg/msvc/RetroArch-Xbox1/RetroArch-Xbox1.vcproj b/pkg/msvc/RetroArch-Xbox1/RetroArch-Xbox1.vcproj index 120cbb84aa..0da7a53238 100644 --- a/pkg/msvc/RetroArch-Xbox1/RetroArch-Xbox1.vcproj +++ b/pkg/msvc/RetroArch-Xbox1/RetroArch-Xbox1.vcproj @@ -22,7 +22,7 @@ Optimization="3" OptimizeForProcessor="2" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\msvc-71";"$(SolutionDir)\..\..\deps\zlib"" - PreprocessorDefinitions="_DEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;HAVE_GRIFFIN;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" + PreprocessorDefinitions="_DEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" MinimalRebuild="TRUE" BasicRuntimeChecks="0" RuntimeLibrary="1" @@ -72,7 +72,7 @@ OmitFramePointers="TRUE" OptimizeForProcessor="2" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\msvc-71";"$(SolutionDir)\..\..\deps\zlib"" - PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;PROFILE;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" + PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;PROFILE;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" StringPooling="TRUE" RuntimeLibrary="0" BufferSecurityCheck="TRUE" @@ -127,7 +127,7 @@ OmitFramePointers="TRUE" OptimizeForProcessor="2" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\msvc-71";"$(SolutionDir)\..\..\deps\zlib"" - PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;PROFILE;FASTCAP;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" + PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;PROFILE;FASTCAP;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" StringPooling="TRUE" RuntimeLibrary="0" BufferSecurityCheck="TRUE" @@ -188,7 +188,7 @@ EnableFiberSafeOptimizations="TRUE" OptimizeForProcessor="2" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\msvc-71";"$(SolutionDir)\..\..\deps\zlib"" - PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;HAVE_GRIFFIN;HAVE_LANGEXTRA;inline=_inline;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" + PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;inline=_inline;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" StringPooling="TRUE" RuntimeLibrary="0" BufferSecurityCheck="TRUE" @@ -241,7 +241,7 @@ OmitFramePointers="TRUE" OptimizeForProcessor="2" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\msvc-71";"$(SolutionDir)\..\..\deps\zlib"" - PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;LTCG;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" + PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;LTCG;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" StringPooling="TRUE" RuntimeLibrary="0" BufferSecurityCheck="TRUE" @@ -300,7 +300,7 @@ EnableFiberSafeOptimizations="TRUE" OptimizeForProcessor="2" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\msvc-71";"$(SolutionDir)\..\..\deps\zlib"" - PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;HAVE_GRIFFIN;HAVE_LANGEXTRA;inline=_inline;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" + PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;inline=_inline;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" StringPooling="TRUE" RuntimeLibrary="0" BufferSecurityCheck="TRUE" @@ -353,7 +353,7 @@ OmitFramePointers="TRUE" OptimizeForProcessor="2" AdditionalIncludeDirectories=""$(SolutionDir)\..\..\libretro-common\include";"$(SolutionDir)\..\..\deps";"$(SolutionDir)\..\..\deps\stb";"$(SolutionDir)\..\..\libretro-common\include\compat\msvc";"$(SolutionDir)\msvc-71";"$(SolutionDir)\..\..\deps\zlib"" - PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;LTCG;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" + PreprocessorDefinitions="NDEBUG;_XBOX;_XBOX1;HAVE_RGUI;HAVE_MENU;HAVE_OVERLAY;RARCH_CONSOLE;HAVE_XINPUT_XBOX1;__STDC_CONSTANT_MACROS;HAVE_ZLIB;LTCG;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_LANGEXTRA;HAVE_RARCH_EXEC;HAVE_DSOUND;HAVE_CC_RESAMPLER;HAVE_D3D;HAVE_D3D8;RARCH_INTERNAL;WANT_ZLIB;HAVE_THREADS;HAVE_RPNG;HAVE_FILTERS_BUILTIN" StringPooling="TRUE" RuntimeLibrary="0" BufferSecurityCheck="TRUE" From be0c118c87690acb3d736d78bb03ec9863bb84c6 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 01:18:40 +0200 Subject: [PATCH 225/517] (task_database.c) Don't use msg_hash_calcualte and msg_hash_to_file_type --- tasks/task_database.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/tasks/task_database.c b/tasks/task_database.c index eb8f53df05..7d24b2f7be 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -564,11 +564,50 @@ end: free(path); } +static enum msg_file_type extension_to_file_type(const char *ext) +{ + if ( + string_is_equal(ext, "7z") || + string_is_equal(ext, "7Z") || + string_is_equal(ext, "zip") || + string_is_equal(ext, "ZIP") || + string_is_equal(ext, "apk") || + string_is_equal(ext, "apk") + ) + return FILE_TYPE_COMPRESSED; + if ( + string_is_equal(ext, "cue") || + string_is_equal(ext, "CUE") + ) + return FILE_TYPE_CUE; + if ( + string_is_equal(ext, "gdi") || + string_is_equal(ext, "GDI") + ) + return FILE_TYPE_GDI; + if ( + string_is_equal(ext, "iso") || + string_is_equal(ext, "ISO") + ) + return FILE_TYPE_ISO; + if ( + string_is_equal(ext, "chd") || + string_is_equal(ext, "CHD") + ) + return FILE_TYPE_CHD; + if ( + string_is_equal(ext, "lutro") || + string_is_equal(ext, "LUTRO") + ) + return FILE_TYPE_LUTRO; + return FILE_TYPE_NONE; +} + static int task_database_iterate_playlist( database_state_handle_t *db_state, database_info_handle_t *db, const char *name) { - switch (msg_hash_to_file_type(msg_hash_calculate(path_get_extension(name)))) + switch (extension_to_file_type(path_get_extension(name))) { case FILE_TYPE_COMPRESSED: #ifdef HAVE_COMPRESSION From 47a6f0bb7a081b6a240f8a069e6672387ffed854 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 02:00:38 +0200 Subject: [PATCH 226/517] Create extension_to_file_hash_type --- menu/menu_displaylist.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index c787a83c10..c8ab47535b 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -1565,6 +1565,17 @@ error: return -1; } +static enum msg_file_type extension_to_file_hash_type(const char *ext) +{ + if (string_is_equal(ext, "sha1")) + return FILE_TYPE_SHA1; + else if (string_is_equal(ext, "crc")) + return FILE_TYPE_CRC; + else if (string_is_equal(ext, "md5")) + return FILE_TYPE_MD5; + return FILE_TYPE_UNKNOWN; +} + static int menu_displaylist_parse_database_entry(menu_displaylist_info_t *info) { unsigned i, j, k; @@ -1653,7 +1664,7 @@ static int menu_displaylist_parse_database_entry(menu_displaylist_info_t *info) const char *elem0 = tmp_str_list->elems[0].data; const char *elem1 = tmp_str_list->elems[1].data; - switch (msg_hash_to_file_type(msg_hash_calculate(elem1))) + switch (extension_to_file_hash_type(elem1)) { case FILE_TYPE_CRC: if (string_is_equal(crc_str, elem0)) From 06bafe3cc383af656fafaa9e50e4ceee3f395b3e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 02:41:09 +0200 Subject: [PATCH 227/517] Fix typo --- tasks/task_database.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/task_database.c b/tasks/task_database.c index 7d24b2f7be..0f949b15c0 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -572,7 +572,7 @@ static enum msg_file_type extension_to_file_type(const char *ext) string_is_equal(ext, "zip") || string_is_equal(ext, "ZIP") || string_is_equal(ext, "apk") || - string_is_equal(ext, "apk") + string_is_equal(ext, "APK") ) return FILE_TYPE_COMPRESSED; if ( From 7ccecc80c119e5f7a36d6b4dfed9b55c9fc9933b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 02:43:35 +0200 Subject: [PATCH 228/517] Update samples/tasks/database --- samples/tasks/database/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/samples/tasks/database/Makefile b/samples/tasks/database/Makefile index f7741d9a4f..b6c8f22d7f 100644 --- a/samples/tasks/database/Makefile +++ b/samples/tasks/database/Makefile @@ -72,10 +72,16 @@ SOURCES_C := \ $(CORE_DIR)/file_path_str.c \ $(CORE_DIR)/playlist.c \ $(CORE_DIR)/verbosity.c \ + $(CORE_DIR)/libretro-db/bintree.c \ + $(CORE_DIR)/libretro-db/libretrodb.c \ + $(CORE_DIR)/libretro-db/query.c \ + $(CORE_DIR)/libretro-db/rmsgpack.c \ + $(CORE_DIR)/libretro-db/rmsgpack_dom.c \ $(LIBRETRO_COMM_DIR)/file/archive_file.c \ $(LIBRETRO_COMM_DIR)/file/config_file.c \ $(LIBRETRO_COMM_DIR)/file/file_path.c \ $(LIBRETRO_COMM_DIR)/file/retro_dirent.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_fnmatch.c \ $(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \ $(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \ $(LIBRETRO_COMM_DIR)/compat/compat_strl.c \ From 8ca8f92aeb83058a26916741a0329fb58a00e301 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 03:02:10 +0200 Subject: [PATCH 229/517] No more configuration.h dependencies in core_info.c --- command.c | 3 ++- core_info.c | 33 ++++++++++++--------------------- core_info.h | 4 ++-- menu/cbs/menu_cbs_ok.c | 5 ++++- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/command.c b/command.c index ab871166c0..b89e4d44e9 100644 --- a/command.c +++ b/command.c @@ -2206,7 +2206,8 @@ TODO: Add a setting for these tweaks */ command_event(CMD_EVENT_CORE_INFO_DEINIT, NULL); if (!string_is_empty(settings->paths.directory_libretro)) - core_info_init_list(); + core_info_init_list(settings->paths.path_libretro_info, + settings->paths.directory_libretro); } break; case CMD_EVENT_CORE_DEINIT: diff --git a/core_info.c b/core_info.c index 76b979046b..32f29e8f9f 100644 --- a/core_info.c +++ b/core_info.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -31,7 +32,6 @@ #include "config.def.h" #include "core_info.h" -#include "configuration.h" #include "file_path_special.h" #include "list_special.h" @@ -224,21 +224,18 @@ static bool core_info_list_iterate( return true; } -static core_info_list_t *core_info_list_new(const char *path) +static core_info_list_t *core_info_list_new(const char *path, const char *libretro_info_dir) { size_t i; core_info_t *core_info = NULL; core_info_list_t *core_info_list = NULL; + const char *path_basedir = libretro_info_dir; struct string_list *contents = dir_list_new_special( path, DIR_LIST_CORES, NULL); - settings_t *settings = config_get_ptr(); - const char *path_basedir = !string_is_empty(settings->paths.path_libretro_info) ? - settings->paths.path_libretro_info : settings->paths.directory_libretro; if (!contents) return NULL; - core_info_list = (core_info_list_t*)calloc(1, sizeof(*core_info_list)); if (!core_info_list) goto error; @@ -635,14 +632,10 @@ void core_info_deinit_list(void) core_info_curr_list = NULL; } -bool core_info_init_list(void) +bool core_info_init_list(const char *path_info, const char *dir_cores) { - settings_t *settings = config_get_ptr(); - - if (settings) - core_info_curr_list = core_info_list_new(settings->paths.directory_libretro); - - if (!core_info_curr_list) + if (!(core_info_curr_list = core_info_list_new(dir_cores, + !string_is_empty(path_info) ? path_info : dir_cores))) return false; return true; } @@ -751,15 +744,13 @@ void core_info_list_get_supported_cores(core_info_list_t *core_info_list, *num_infos = supported; } -void core_info_get_name(const char *path, char *s, size_t len) +void core_info_get_name(const char *path, char *s, size_t len, + const char *path_info, const char *dir_cores) { size_t i; - settings_t *settings = config_get_ptr(); - struct string_list *contents = dir_list_new_special( - settings->paths.directory_libretro, - DIR_LIST_CORES, NULL); - const char *path_basedir = !string_is_empty(settings->paths.path_libretro_info) ? - settings->paths.path_libretro_info : settings->paths.directory_libretro; + const char *path_basedir = !string_is_empty(path_info) ? + path_info : dir_cores; + struct string_list *contents = dir_list_new_special(dir_cores, DIR_LIST_CORES, NULL); if (!contents) return; @@ -786,7 +777,7 @@ void core_info_get_name(const char *path, char *s, size_t len) continue; } - conf = config_file_new(info_path); + conf = config_file_new(info_path); if (!conf) { diff --git a/core_info.h b/core_info.h index c058e8d937..57e6c8bf2f 100644 --- a/core_info.h +++ b/core_info.h @@ -97,7 +97,7 @@ bool core_info_list_get_display_name(core_info_list_t *list, bool core_info_get_display_name(const char *path, char *s, size_t len); -void core_info_get_name(const char *path, char *s, size_t len); +void core_info_get_name(const char *path, char *s, size_t len, const char *path_info, const char *dir_cores); core_info_t *core_info_get(core_info_list_t *list, size_t i); @@ -109,7 +109,7 @@ bool core_info_get_current_core(core_info_t **core); void core_info_deinit_list(void); -bool core_info_init_list(void); +bool core_info_init_list(const char *path_info, const char *dir_cores); bool core_info_get_list(core_info_list_t **core); diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 6b22dbd473..fb75d34b04 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -2267,6 +2267,7 @@ static int action_ok_core_deferred_set(const char *new_core_path, const char *content_label, unsigned type, size_t idx, size_t entry_idx) { char core_display_name[PATH_MAX_LENGTH]; + settings_t *settings = config_get_ptr(); menu_handle_t *menu = NULL; size_t selection = menu_navigation_get_selection(); @@ -2276,7 +2277,9 @@ static int action_ok_core_deferred_set(const char *new_core_path, core_display_name[0] = '\0'; core_info_get_name(new_core_path, - core_display_name, sizeof(core_display_name)); + core_display_name, sizeof(core_display_name), + settings->paths.path_libretro_info, + settings->paths.directory_libretro); command_playlist_update_write( NULL, menu->rdb_entry_start_game_selection_ptr, From 6e46d1deb154ea9fb79be50a73426f57e81c1893 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 03:07:28 +0200 Subject: [PATCH 230/517] Buildfix --- menu/menu_displaylist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index c8ab47535b..1b0176c11c 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -1573,7 +1573,7 @@ static enum msg_file_type extension_to_file_hash_type(const char *ext) return FILE_TYPE_CRC; else if (string_is_equal(ext, "md5")) return FILE_TYPE_MD5; - return FILE_TYPE_UNKNOWN; + return FILE_TYPE_NONE; } static int menu_displaylist_parse_database_entry(menu_displaylist_info_t *info) From 00cc4ca7aa823bd218a7d3ff74b93b2215c7cef4 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 03:53:46 +0200 Subject: [PATCH 231/517] (samples) Gets close to linking now --- intl/msg_hash_us.c | 5 ++++- msg_hash.c | 4 ++++ samples/tasks/database/Makefile | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 264b0a7860..f399039c77 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -22,9 +22,11 @@ #include #include "../msg_hash.h" -#include "../configuration.h" #include "../verbosity.h" +#ifdef RARCH_INTERNAL +#include "../configuration.h" + int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) { settings_t *settings = config_get_ptr(); @@ -2029,6 +2031,7 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) return 0; } +#endif #ifdef HAVE_MENU static const char *menu_hash_to_str_us_label_enum(enum msg_hash_enums msg) diff --git a/msg_hash.c b/msg_hash.c index 37a871b222..afdc0c4d0f 100644 --- a/msg_hash.c +++ b/msg_hash.c @@ -31,6 +31,7 @@ static unsigned uint_user_language; int menu_hash_get_help_enum(enum msg_hash_enums msg, char *s, size_t len) { +#ifdef HAVE_MENU int ret = -1; #ifdef HAVE_LANGEXTRA @@ -90,6 +91,9 @@ int menu_hash_get_help_enum(enum msg_hash_enums msg, char *s, size_t len) return ret; return menu_hash_get_help_us_enum(msg, s, len); +#else + return 0; +#endif } const char *msg_hash_to_str(enum msg_hash_enums msg) diff --git a/samples/tasks/database/Makefile b/samples/tasks/database/Makefile index b6c8f22d7f..3529195a0b 100644 --- a/samples/tasks/database/Makefile +++ b/samples/tasks/database/Makefile @@ -70,6 +70,8 @@ SOURCES_C := \ $(CORE_DIR)/database_info.c \ $(CORE_DIR)/core_info.c \ $(CORE_DIR)/file_path_str.c \ + $(CORE_DIR)/msg_hash.c \ + $(CORE_DIR)/intl/msg_hash_us.c \ $(CORE_DIR)/playlist.c \ $(CORE_DIR)/verbosity.c \ $(CORE_DIR)/libretro-db/bintree.c \ @@ -81,6 +83,7 @@ SOURCES_C := \ $(LIBRETRO_COMM_DIR)/file/config_file.c \ $(LIBRETRO_COMM_DIR)/file/file_path.c \ $(LIBRETRO_COMM_DIR)/file/retro_dirent.c \ + $(LIBRETRO_COMM_DIR)/hash/rhash.c \ $(LIBRETRO_COMM_DIR)/compat/compat_fnmatch.c \ $(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \ $(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \ From 3016edce0afc65adc5aae8f540b2bceb00d35d11 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 04:00:01 +0200 Subject: [PATCH 232/517] Move retroarch.h dependencies out of core_info.c --- core_info.c | 13 +++++++------ core_info.h | 3 ++- menu/menu_displaylist.c | 11 ++++++++++- tasks/task_content.c | 9 ++++++++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/core_info.c b/core_info.c index 32f29e8f9f..71d00a8c49 100644 --- a/core_info.c +++ b/core_info.c @@ -27,7 +27,6 @@ #include "config.h" #endif -#include "retroarch.h" #include "verbosity.h" #include "config.def.h" @@ -515,7 +514,8 @@ static core_info_t *core_info_find_internal( static bool core_info_list_update_missing_firmware_internal( core_info_list_t *core_info_list, const char *core, - const char *systemdir) + const char *systemdir, + bool *set_missing_bios) { size_t i; core_info_t *info = NULL; @@ -532,7 +532,6 @@ static bool core_info_list_update_missing_firmware_internal( path = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); path[0] = '\0'; - rarch_ctl(RARCH_CTL_UNSET_MISSING_BIOS, NULL); for (i = 0; i < info->firmware_count; i++) { @@ -544,7 +543,7 @@ static bool core_info_list_update_missing_firmware_internal( info->firmware[i].missing = !filestream_exists(path); if (info->firmware[i].missing && !info->firmware[i].optional) { - rarch_ctl(RARCH_CTL_SET_MISSING_BIOS, NULL); + *set_missing_bios = true; RARCH_WARN("Firmware missing: %s\n", info->firmware[i].path); } } @@ -648,13 +647,15 @@ bool core_info_get_list(core_info_list_t **core) return true; } -bool core_info_list_update_missing_firmware(core_info_ctx_firmware_t *info) +bool core_info_list_update_missing_firmware(core_info_ctx_firmware_t *info, + bool *set_missing_bios) { if (!info) return false; return core_info_list_update_missing_firmware_internal( core_info_curr_list, - info->path, info->directory.system); + info->path, info->directory.system, + set_missing_bios); } bool core_info_load(core_info_ctx_find_t *info) diff --git a/core_info.h b/core_info.h index 57e6c8bf2f..b1b6b2f102 100644 --- a/core_info.h +++ b/core_info.h @@ -113,7 +113,8 @@ bool core_info_init_list(const char *path_info, const char *dir_cores); bool core_info_get_list(core_info_list_t **core); -bool core_info_list_update_missing_firmware(core_info_ctx_firmware_t *info); +bool core_info_list_update_missing_firmware(core_info_ctx_firmware_t *info, + bool *set_missing_bios); bool core_info_find(core_info_ctx_find_t *info, const char *name); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 1b0176c11c..043fcb9b6e 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -285,12 +285,21 @@ static int menu_displaylist_parse_core_info(menu_displaylist_info_t *info) if (core_info->firmware_count > 0) { core_info_ctx_firmware_t firmware_info; + bool update_missing_firmware = false; + bool set_missing_firmware = false; settings_t *settings = config_get_ptr(); firmware_info.path = core_info->path; firmware_info.directory.system = settings->paths.directory_system; - if (core_info_list_update_missing_firmware(&firmware_info)) + rarch_ctl(RARCH_CTL_UNSET_MISSING_BIOS, NULL); + + update_missing_firmware = core_info_list_update_missing_firmware(&firmware_info, &set_missing_firmware); + + if (set_missing_firmware) + rarch_ctl(RARCH_CTL_SET_MISSING_BIOS, NULL); + + if (update_missing_firmware) { fill_pathname_noext(tmp, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_FIRMWARE), diff --git a/tasks/task_content.c b/tasks/task_content.c index 2c5f250482..273caa570f 100644 --- a/tasks/task_content.c +++ b/tasks/task_content.c @@ -1009,6 +1009,7 @@ static bool firmware_update_status( content_information_ctx_t *content_ctx) { core_info_ctx_firmware_t firmware_info; + bool set_missing_firmware = false; core_info_t *core_info = NULL; size_t s_size = PATH_MAX_LENGTH * sizeof(char); char *s = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); @@ -1032,7 +1033,13 @@ static bool firmware_update_status( RARCH_LOG("Updating firmware status for: %s on %s\n", core_info->path, firmware_info.directory.system); - core_info_list_update_missing_firmware(&firmware_info); + + rarch_ctl(RARCH_CTL_UNSET_MISSING_BIOS, NULL); + + core_info_list_update_missing_firmware(&firmware_info, &set_missing_firmware); + + if (set_missing_firmware) + rarch_ctl(RARCH_CTL_SET_MISSING_BIOS, NULL); if( content_ctx->bios_is_missing && From 6f0fc2426dc13f530836c7235b6729d7c5b8274a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 04:13:45 +0200 Subject: [PATCH 233/517] (database_info/task_database) Get rid of list_special dependencies --- core_info.c | 3 ++- database_info.c | 17 ++++++++++++----- database_info.h | 3 ++- menu/cbs/menu_cbs_ok.c | 4 +++- menu/cbs/menu_cbs_scan.c | 8 ++++++-- tasks/task_database.c | 15 ++++++++++----- tasks/tasks_internal.h | 3 ++- 7 files changed, 37 insertions(+), 16 deletions(-) diff --git a/core_info.c b/core_info.c index 71d00a8c49..a5798ec0c9 100644 --- a/core_info.c +++ b/core_info.c @@ -751,7 +751,8 @@ void core_info_get_name(const char *path, char *s, size_t len, size_t i; const char *path_basedir = !string_is_empty(path_info) ? path_info : dir_cores; - struct string_list *contents = dir_list_new_special(dir_cores, DIR_LIST_CORES, NULL); + struct string_list *contents = dir_list_new_special( + dir_cores, DIR_LIST_CORES, NULL); if (!contents) return; diff --git a/database_info.c b/database_info.c index 007fce58c4..894ae250ac 100644 --- a/database_info.c +++ b/database_info.c @@ -22,12 +22,13 @@ #include #include #include +#include #include #include "libretro-db/libretrodb.h" +#include "core_info.h" #include "database_info.h" -#include "list_special.h" #include "verbosity.h" int database_info_build_query_enum(char *s, size_t len, @@ -367,16 +368,22 @@ static void dir_list_prioritize(struct string_list *list) } database_info_handle_t *database_info_dir_init(const char *dir, - enum database_type type, retro_task_t *task) + enum database_type type, retro_task_t *task, + bool show_hidden_files) { - struct string_list *list = NULL; - database_info_handle_t *db = (database_info_handle_t*) + core_info_list_t *core_info_list = NULL; + struct string_list *list = NULL; + database_info_handle_t *db = (database_info_handle_t*) calloc(1, sizeof(*db)); if (!db) return NULL; - list = dir_list_new_special(dir, DIR_LIST_RECURSIVE, NULL); + core_info_get_list(&core_info_list); + + list = dir_list_new(dir, core_info_list->all_ext, + false, show_hidden_files, + false, true); if (!list) { diff --git a/database_info.h b/database_info.h index 2664577e6f..aa0f5b8011 100644 --- a/database_info.h +++ b/database_info.h @@ -126,7 +126,8 @@ database_info_list_t *database_info_list_new(const char *rdb_path, void database_info_list_free(database_info_list_t *list); database_info_handle_t *database_info_dir_init(const char *dir, - enum database_type type, retro_task_t *task); + enum database_type type, retro_task_t *task, + bool show_hidden_files); database_info_handle_t *database_info_file_init(const char *path, enum database_type type, retro_task_t *task); diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index fb75d34b04..75245ef690 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -983,7 +983,9 @@ static void content_add_to_playlist(const char *path) task_push_dbscan( settings->paths.directory_playlist, settings->paths.path_content_database, - path, false, handle_dbscan_finished); + path, false, + settings->bools.show_hidden_files, + handle_dbscan_finished); #endif } diff --git a/menu/cbs/menu_cbs_scan.c b/menu/cbs/menu_cbs_scan.c index c878491f29..150f226fa3 100644 --- a/menu/cbs/menu_cbs_scan.c +++ b/menu/cbs/menu_cbs_scan.c @@ -64,7 +64,9 @@ int action_scan_file(const char *path, task_push_dbscan( settings->paths.directory_playlist, settings->paths.path_content_database, - fullpath, false, handle_dbscan_finished); + fullpath, false, + settings->bools.show_hidden_files, + handle_dbscan_finished); return 0; } @@ -90,7 +92,9 @@ int action_scan_directory(const char *path, task_push_dbscan( settings->paths.directory_playlist, settings->paths.path_content_database, - fullpath, true, handle_dbscan_finished); + fullpath, true, + settings->bools.show_hidden_files, + handle_dbscan_finished); return 0; } diff --git a/tasks/task_database.c b/tasks/task_database.c index 0f949b15c0..76f2b01a58 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -31,7 +31,6 @@ #include "../database_info.h" #include "../file_path_special.h" -#include "../list_special.h" #include "../msg_hash.h" #include "../playlist.h" #ifdef RARCH_INTERNAL @@ -61,6 +60,7 @@ typedef struct db_handle { bool is_directory; bool scan_started; + bool show_hidden_files; unsigned status; char *playlist_directory; char *content_database_path; @@ -1142,7 +1142,7 @@ static void task_database_handler(retro_task_t *task) if (!string_is_empty(db->fullpath)) { if (db->is_directory) - db->handle = database_info_dir_init(db->fullpath, DATABASE_TYPE_ITERATE, task); + db->handle = database_info_dir_init(db->fullpath, DATABASE_TYPE_ITERATE, task, db->show_hidden_files); else db->handle = database_info_file_init(db->fullpath, DATABASE_TYPE_ITERATE, task); } @@ -1165,9 +1165,11 @@ static void task_database_handler(retro_task_t *task) if (dbstate && !dbstate->list) { if (!string_is_empty(db->content_database_path)) - dbstate->list = dir_list_new_special( + dbstate->list = dir_list_new( db->content_database_path, - DIR_LIST_DATABASES, NULL); + "rdb", false, + db->show_hidden_files, + false, false); /* If the scan path matches a database path exactly then * save time by only processing that database. */ @@ -1287,7 +1289,9 @@ bool task_push_dbscan( const char *playlist_directory, const char *content_database, const char *fullpath, - bool directory, retro_task_callback_t cb) + bool directory, + bool show_hidden_files, + retro_task_callback_t cb) { retro_task_t *t = (retro_task_t*)calloc(1, sizeof(*t)); db_handle_t *db = (db_handle_t*)calloc(1, sizeof(db_handle_t)); @@ -1300,6 +1304,7 @@ bool task_push_dbscan( t->callback = cb; t->title = strdup(msg_hash_to_str(MSG_PREPARING_FOR_CONTENT_SCAN)); + db->show_hidden_files = show_hidden_files; db->is_directory = directory; db->playlist_directory = NULL; db->fullpath = strdup(fullpath); diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index 9b6c0ff9d2..4e8e4e5a9f 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -133,7 +133,8 @@ bool task_push_dbscan( const char *playlist_directory, const char *content_database, const char *fullpath, - bool directory, retro_task_callback_t cb); + bool directory, bool show_hidden_files, + retro_task_callback_t cb); #endif #ifdef HAVE_OVERLAY From c9e48cd9de466885bdc3ef0a8077d6ed3e2e30ba Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 04:31:19 +0200 Subject: [PATCH 234/517] Cleanups / get rid of more dependencies --- command.c | 10 +++++++++- core_info.c | 30 ++++++++++++++++++------------ core_info.h | 7 +++++-- menu/cbs/menu_cbs_ok.c | 10 +++++++++- tasks/task_database.c | 2 +- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/command.c b/command.c index b89e4d44e9..13f0e9cf74 100644 --- a/command.c +++ b/command.c @@ -2202,12 +2202,20 @@ TODO: Add a setting for these tweaks */ break; case CMD_EVENT_CORE_INFO_INIT: { + char ext_name[255]; + ext_name[0] = '\0'; settings_t *settings = config_get_ptr(); command_event(CMD_EVENT_CORE_INFO_DEINIT, NULL); + if (!frontend_driver_get_core_extension(ext_name, sizeof(ext_name))) + return false; + if (!string_is_empty(settings->paths.directory_libretro)) core_info_init_list(settings->paths.path_libretro_info, - settings->paths.directory_libretro); + settings->paths.directory_libretro, + ext_name, + settings->bools.show_hidden_files + ); } break; case CMD_EVENT_CORE_DEINIT: diff --git a/core_info.c b/core_info.c index a5798ec0c9..d7ed2041d6 100644 --- a/core_info.c +++ b/core_info.c @@ -29,10 +29,8 @@ #include "verbosity.h" -#include "config.def.h" #include "core_info.h" #include "file_path_special.h" -#include "list_special.h" static const char *core_info_tmp_path = NULL; static const struct string_list *core_info_tmp_list = NULL; @@ -223,15 +221,20 @@ static bool core_info_list_iterate( return true; } -static core_info_list_t *core_info_list_new(const char *path, const char *libretro_info_dir) +static core_info_list_t *core_info_list_new(const char *path, + const char *libretro_info_dir, + const char *exts, + bool show_hidden_files) { size_t i; core_info_t *core_info = NULL; core_info_list_t *core_info_list = NULL; const char *path_basedir = libretro_info_dir; - struct string_list *contents = dir_list_new_special( - path, DIR_LIST_CORES, NULL); - + struct string_list *contents = dir_list_new( + path, exts, + false, + show_hidden_files, + false, false); if (!contents) return NULL; @@ -631,10 +634,13 @@ void core_info_deinit_list(void) core_info_curr_list = NULL; } -bool core_info_init_list(const char *path_info, const char *dir_cores) +bool core_info_init_list(const char *path_info, const char *dir_cores, + const char *exts, bool show_hidden_files) { if (!(core_info_curr_list = core_info_list_new(dir_cores, - !string_is_empty(path_info) ? path_info : dir_cores))) + !string_is_empty(path_info) ? path_info : dir_cores, + exts, + show_hidden_files))) return false; return true; } @@ -746,14 +752,14 @@ void core_info_list_get_supported_cores(core_info_list_t *core_info_list, } void core_info_get_name(const char *path, char *s, size_t len, - const char *path_info, const char *dir_cores) + const char *path_info, const char *dir_cores, + const char *exts, bool show_hidden_files) { size_t i; const char *path_basedir = !string_is_empty(path_info) ? path_info : dir_cores; - struct string_list *contents = dir_list_new_special( - dir_cores, DIR_LIST_CORES, NULL); - + struct string_list *contents = dir_list_new( + dir_cores, exts, false, show_hidden_files, false, false); if (!contents) return; diff --git a/core_info.h b/core_info.h index b1b6b2f102..b63fa8df98 100644 --- a/core_info.h +++ b/core_info.h @@ -97,7 +97,9 @@ bool core_info_list_get_display_name(core_info_list_t *list, bool core_info_get_display_name(const char *path, char *s, size_t len); -void core_info_get_name(const char *path, char *s, size_t len, const char *path_info, const char *dir_cores); +void core_info_get_name(const char *path, char *s, size_t len, + const char *path_info, const char *dir_cores, + const char *exts, bool show_hidden_files); core_info_t *core_info_get(core_info_list_t *list, size_t i); @@ -109,7 +111,8 @@ bool core_info_get_current_core(core_info_t **core); void core_info_deinit_list(void); -bool core_info_init_list(const char *path_info, const char *dir_cores); +bool core_info_init_list(const char *path_info, const char *dir_cores, + const char *exts, bool show_hidden_files); bool core_info_get_list(core_info_list_t **core); diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 75245ef690..d3625fe160 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -2268,20 +2268,28 @@ static int action_ok_path_scan_directory(const char *path, static int action_ok_core_deferred_set(const char *new_core_path, const char *content_label, unsigned type, size_t idx, size_t entry_idx) { + char ext_name[255]; char core_display_name[PATH_MAX_LENGTH]; settings_t *settings = config_get_ptr(); menu_handle_t *menu = NULL; size_t selection = menu_navigation_get_selection(); + ext_name[0] = '\0'; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); + if (!frontend_driver_get_core_extension(ext_name, sizeof(ext_name))) + return menu_cbs_exit(); + core_display_name[0] = '\0'; core_info_get_name(new_core_path, core_display_name, sizeof(core_display_name), settings->paths.path_libretro_info, - settings->paths.directory_libretro); + settings->paths.directory_libretro, + ext_name, + settings->bools.show_hidden_files); command_playlist_update_write( NULL, menu->rdb_entry_start_game_selection_ptr, diff --git a/tasks/task_database.c b/tasks/task_database.c index 76f2b01a58..a30e6ef863 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -28,6 +28,7 @@ #include #include "tasks_internal.h" +#include "../core_info.h" #include "../database_info.h" #include "../file_path_special.h" @@ -37,7 +38,6 @@ #include "../retroarch.h" #endif #include "../verbosity.h" -#include "../core_info.h" #ifndef COLLECTION_SIZE #define COLLECTION_SIZE 99999 From d66b8fa882840e7c00850350ebbb7ea3bb5a615b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 04:53:12 +0200 Subject: [PATCH 235/517] Start implementing main function --- samples/tasks/database/Makefile | 6 ++-- samples/tasks/database/main.c | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 samples/tasks/database/main.c diff --git a/samples/tasks/database/Makefile b/samples/tasks/database/Makefile index 3529195a0b..fb32f20c1e 100644 --- a/samples/tasks/database/Makefile +++ b/samples/tasks/database/Makefile @@ -65,6 +65,7 @@ flags += -std=c99 INCFLAGS := -I$(LIBRETRO_COMM_DIR)/include SOURCES_C := \ + $(CORE_DIR)/samples/tasks/database/main.c \ $(CORE_DIR)/tasks/task_database.c \ $(CORE_DIR)/tasks/task_database_cue.c \ $(CORE_DIR)/database_info.c \ @@ -99,9 +100,10 @@ SOURCES_C := \ $(LIBRETRO_COMM_DIR)/streams/file_stream.c \ $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c -#SOURCES_C += $(CORE_DIR)/msg_hash.c +DEFINES = -DHAVE_LIBRETRODB -DEFINES = +CFLAGS += $(DEFINES) +CXXFLAGS += $(DEFINES) OBJECTS = $(SOURCES_C:.c=.o) diff --git a/samples/tasks/database/main.c b/samples/tasks/database/main.c new file mode 100644 index 0000000000..fa88fb5246 --- /dev/null +++ b/samples/tasks/database/main.c @@ -0,0 +1,51 @@ +#include +#include +#include + +#include + +#include "../../../core_info.h" +#include "../../../tasks/tasks_internal.h" + +/* + * return codes - + * graceful exit: 1 + * normal exit: 0 + * error exit: -1 + */ + +int main(int argc, char *argv[]) +{ + const char *db_dir = NULL; + const char *core_info_dir = NULL; + const char *core_dir = NULL; + const char *input_dir = NULL; + const char *playlist_dir = NULL; + const char *exts = "dll"; + + if (argc < 6) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + db_dir = argv[1]; + core_dir = argv[2]; + core_info_dir = argv[3]; + input_dir = argv[4]; + playlist_dir = argv[5]; + + task_queue_init(false /* threaded enable */, NULL); + + core_info_init_list(core_info_dir, core_dir, exts, true); + + task_push_dbscan(playlist_dir, db_dir, input_dir, false, + true, NULL /* bind callback here later */); + + task_queue_check(); + + core_info_deinit_list(); + task_queue_deinit(); + + return 0; +} From c24179dc63a3e4ff31b60ab8b4e59509da67cdd8 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 05:16:33 +0200 Subject: [PATCH 236/517] Scanning should now work in standalone program --- samples/tasks/database/main.c | 32 ++++++++++++++++++++++++++++---- tasks/task_database.c | 20 ++++++++++++++++---- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/samples/tasks/database/main.c b/samples/tasks/database/main.c index fa88fb5246..9d3325a67f 100644 --- a/samples/tasks/database/main.c +++ b/samples/tasks/database/main.c @@ -7,6 +7,15 @@ #include "../../../core_info.h" #include "../../../tasks/tasks_internal.h" +static bool loop_active = true; + +static void main_msg_queue_push(const char *msg, + unsigned prio, unsigned duration, + bool flush) +{ + fprintf(stderr, "MSGQ: %s\n", msg); +} + /* * return codes - * graceful exit: 1 @@ -14,6 +23,12 @@ * error exit: -1 */ +static void main_db_cb(void *task_data, void *user_data, const char *err) +{ + fprintf(stderr, "DB CB: %s\n", err); + loop_active = false; +} + int main(int argc, char *argv[]) { const char *db_dir = NULL; @@ -35,14 +50,23 @@ int main(int argc, char *argv[]) input_dir = argv[4]; playlist_dir = argv[5]; - task_queue_init(false /* threaded enable */, NULL); + fprintf(stderr, "RDB database dir: %s\n", db_dir); + fprintf(stderr, "Core dir: %s\n", core_dir); + fprintf(stderr, "Core info dir: %s\n", core_info_dir); + fprintf(stderr, "Input dir: %s\n", input_dir); + fprintf(stderr, "Playlist dir: %s\n", playlist_dir); + + task_queue_init(false /* threaded enable */, main_msg_queue_push); core_info_init_list(core_info_dir, core_dir, exts, true); - task_push_dbscan(playlist_dir, db_dir, input_dir, false, - true, NULL /* bind callback here later */); + task_push_dbscan(playlist_dir, db_dir, input_dir, true, + true, main_db_cb); - task_queue_check(); + while (loop_active) + task_queue_check(); + + fprintf(stderr, "Exit loop\n"); core_info_deinit_list(); task_queue_deinit(); diff --git a/tasks/task_database.c b/tasks/task_database.c index a30e6ef863..96c0055d8d 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -150,7 +150,7 @@ static int task_database_iterate_start(database_info_handle_t *db, #ifdef RARCH_INTERNAL runloop_msg_queue_push(msg, 1, 180, true); #else - RARCH_LOG("msg: %s\n", msg); + fprintf(stderr, "msg: %s\n", msg); #endif } @@ -741,8 +741,8 @@ static int database_info_list_iterate_new(database_state_handle_t *db_state, { const char *new_database = database_info_get_current_name(db_state); -#if 0 - RARCH_LOG("Check database [%d/%d] : %s\n", (unsigned)db_state->list_index, +#ifndef RARCH_INTERNAL + fprintf(stderr, "Check database [%d/%d] : %s\n", (unsigned)db_state->list_index, (unsigned)db_state->list->size, new_database); #endif if (db_state->info) @@ -808,6 +808,7 @@ static int database_info_list_iterate_found_match( (hash = strchr(entry_path_str, '#'))) *hash = '\0'; +#if defined(RARCH_INTERNAL) #if 0 RARCH_LOG("Found match in database !\n"); @@ -819,6 +820,17 @@ static int database_info_list_iterate_found_match( RARCH_LOG("ZIP entry: %s\n", archive_name); RARCH_LOG("entry path str: %s\n", entry_path_str); #endif +#else + fprintf(stderr, "Found match in database !\n"); + + fprintf(stderr, "Path: %s\n", db_path); + fprintf(stderr, "CRC : %s\n", db_crc); + fprintf(stderr, "Playlist Path: %s\n", db_playlist_path); + fprintf(stderr, "Entry Path: %s\n", entry_path); + fprintf(stderr, "Playlist not NULL: %d\n", playlist != NULL); + fprintf(stderr, "ZIP entry: %s\n", archive_name); + fprintf(stderr, "entry path str: %s\n", entry_path_str); +#endif if(!playlist_entry_exists(playlist, entry_path_str, db_crc)) { @@ -1243,7 +1255,7 @@ static void task_database_handler(retro_task_t *task) #ifdef RARCH_INTERNAL runloop_msg_queue_push(msg, 0, 180, true); #else - RARCH_LOG("msg: %s\n", msg); + fprintf(stderr, "msg: %s\n", msg); #endif goto task_finished; } From 639152e291680514cd08293be4dce345e7d5c812 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 05:20:19 +0200 Subject: [PATCH 237/517] Hardcode exts for now - we assume this standalone program will only be used on Windows/OSX/Linux systems for now --- samples/tasks/database/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/tasks/database/main.c b/samples/tasks/database/main.c index 9d3325a67f..875b18d0cb 100644 --- a/samples/tasks/database/main.c +++ b/samples/tasks/database/main.c @@ -36,7 +36,11 @@ int main(int argc, char *argv[]) const char *core_dir = NULL; const char *input_dir = NULL; const char *playlist_dir = NULL; +#ifdef _WIN32 const char *exts = "dll"; +#else + const char *exts = "so"; +#endif if (argc < 6) { From 25ab35b7d0fbbf8352d39ede34192361f8158c6e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 05:26:47 +0200 Subject: [PATCH 238/517] (samples/tasks/database) Add hardcoded extensions for OSX --- samples/tasks/database/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/tasks/database/main.c b/samples/tasks/database/main.c index 875b18d0cb..49b6274a3e 100644 --- a/samples/tasks/database/main.c +++ b/samples/tasks/database/main.c @@ -36,8 +36,10 @@ int main(int argc, char *argv[]) const char *core_dir = NULL; const char *input_dir = NULL; const char *playlist_dir = NULL; -#ifdef _WIN32 +#if defined(_WIN32) const char *exts = "dll"; +#elif defined(__MACH__) + const char *exts = "dylib"; #else const char *exts = "so"; #endif From 5697a249464dd284d5145f113a345ffa952fea58 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 05:28:58 +0200 Subject: [PATCH 239/517] (samples/tasks/database) Define -DHAVE_COMPRESSION --- samples/tasks/database/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/tasks/database/Makefile b/samples/tasks/database/Makefile index fb32f20c1e..d526f03358 100644 --- a/samples/tasks/database/Makefile +++ b/samples/tasks/database/Makefile @@ -100,7 +100,7 @@ SOURCES_C := \ $(LIBRETRO_COMM_DIR)/streams/file_stream.c \ $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c -DEFINES = -DHAVE_LIBRETRODB +DEFINES = -DHAVE_LIBRETRODB -DHAVE_COMPRESSION CFLAGS += $(DEFINES) CXXFLAGS += $(DEFINES) From 86ea98605d29087e9de820a6ffd971e124dd114e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 06:50:47 +0200 Subject: [PATCH 240/517] (MSVC 2010) Buildfix --- command.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command.c b/command.c index 13f0e9cf74..7e8f38ef42 100644 --- a/command.c +++ b/command.c @@ -2203,8 +2203,10 @@ TODO: Add a setting for these tweaks */ case CMD_EVENT_CORE_INFO_INIT: { char ext_name[255]; - ext_name[0] = '\0'; settings_t *settings = config_get_ptr(); + + ext_name[0] = '\0'; + command_event(CMD_EVENT_CORE_INFO_DEINIT, NULL); if (!frontend_driver_get_core_extension(ext_name, sizeof(ext_name))) From ac511120d45122671e00cbb82f13f92f9aabbf3a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 07:19:27 +0200 Subject: [PATCH 241/517] (retroarch.c) Cleanups --- retroarch.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/retroarch.c b/retroarch.c index fed3ebddc4..537ed82f37 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2838,11 +2838,13 @@ static enum runloop_state runloop_check_state( if (new_button_state && !old_button_state) { - if (input_nonblock_state) { + if (input_nonblock_state) + { input_driver_unset_nonblock_state(); runloop_fastmotion = false; } - else { + else + { input_driver_set_nonblock_state(); runloop_fastmotion = true; } @@ -2850,11 +2852,13 @@ static enum runloop_state runloop_check_state( } else if (old_hold_button_state != new_hold_button_state) { - if (new_hold_button_state) { + if (new_hold_button_state) + { input_driver_set_nonblock_state(); runloop_fastmotion = true; } - else { + else + { input_driver_unset_nonblock_state(); runloop_fastmotion = false; } @@ -2967,21 +2971,17 @@ static enum runloop_state runloop_check_state( if (new_slowmotion_button_state && !old_slowmotion_button_state) { - if (runloop_slowmotion) { + if (runloop_slowmotion) runloop_slowmotion = false; - } - else { + else runloop_slowmotion = true; - } } else if (old_slowmotion_hold_button_state != new_slowmotion_hold_button_state) { - if (new_slowmotion_hold_button_state) { + if (new_slowmotion_hold_button_state) runloop_slowmotion = true; - } - else { + else runloop_slowmotion = false; - } } if (runloop_slowmotion) From 00da8606819cddd8340a0c6e8785b3f3b206b31b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 16:34:33 +0200 Subject: [PATCH 242/517] Cleanup --- menu/drivers/xmb.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index ee8f467dc3..4c979fe0e0 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -1029,19 +1029,22 @@ static void xmb_update_thumbnail_path(void *data, unsigned i, char pos) if (string_is_equal(core_name, "imageviewer")) { - if (pos == 'R' || (pos == 'L' && string_is_equal(xmb_thumbnails_ident('R'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))) + if ( + (pos == 'R') || + ( + pos == 'L' && + string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) + ) + ) { if (!string_is_empty(entry.label)) strlcpy(new_path, entry.label, sizeof(new_path)); - goto end; } else - { xmb->left_thumbnail = 0; - goto end; - } + goto end; } } From bce7742745e23e4437e4c0f840d7bc14f7cedcf0 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 17:40:29 +0200 Subject: [PATCH 243/517] Create playlist_cached functions inside playlist.c --- menu/menu_driver.c | 12 +++--------- playlist.c | 24 ++++++++++++++++++++++++ playlist.h | 6 ++++++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/menu/menu_driver.c b/menu/menu_driver.c index dde5ee212b..403ce0d617 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -182,9 +182,6 @@ static bool menu_driver_pending_shutdown = false; /* Are we binding a button inside the menu? */ static bool menu_driver_is_binding = false; -/* The currently active playlist that we are using inside the menu */ -static playlist_t *menu_driver_playlist = NULL; - static menu_handle_t *menu_driver_data = NULL; static const menu_ctx_driver_t *menu_driver_ctx = NULL; static void *menu_userdata = NULL; @@ -1881,9 +1878,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) menu_driver_pending_shutdown = true; break; case RARCH_MENU_CTL_PLAYLIST_FREE: - if (menu_driver_playlist) - playlist_free(menu_driver_playlist); - menu_driver_playlist = NULL; + playlist_free_cached(); break; case RARCH_MENU_CTL_FIND_DRIVER: { @@ -1930,8 +1925,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) const char *path = (const char*)data; if (string_is_empty(path)) return false; - menu_driver_playlist = playlist_init(path, - COLLECTION_SIZE); + playlist_init_cached(path, COLLECTION_SIZE); } break; case RARCH_MENU_CTL_PLAYLIST_GET: @@ -1939,7 +1933,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) playlist_t **playlist = (playlist_t**)data; if (!playlist) return false; - *playlist = menu_driver_playlist; + *playlist = playlist_get_cached(); } break; case RARCH_MENU_CTL_SET_PREVENT_POPULATE: diff --git a/playlist.c b/playlist.c index 91d8ed032b..fba39fee5f 100644 --- a/playlist.c +++ b/playlist.c @@ -52,6 +52,7 @@ struct content_playlist char *conf_path; struct playlist_entry *entries; }; +static playlist_t *playlist_cached = NULL; typedef int (playlist_sort_fun_t)( const struct playlist_entry *a, @@ -554,6 +555,29 @@ end: return true; } +void playlist_free_cached(void) +{ + playlist_free(playlist_cached); + playlist_cached = NULL; +} + +playlist_t *playlist_get_cached(void) +{ + if (playlist_cached) + return playlist_cached; + return NULL; +} + +bool playlist_init_cached(const char *path, size_t size) +{ + playlist_t *playlist = playlist_init(path, size); + if (!playlist) + return false; + + playlist_cached = playlist; + return true; +} + /** * playlist_init: * @path : Path to playlist contents file. diff --git a/playlist.h b/playlist.h index a130df23c8..d3e8ef1176 100644 --- a/playlist.h +++ b/playlist.h @@ -129,6 +129,12 @@ void playlist_write_file(playlist_t *playlist); void playlist_qsort(playlist_t *playlist); +void playlist_free_cached(void); + +playlist_t *playlist_get_cached(void); + +bool playlist_init_cached(const char *path, size_t size); + RETRO_END_DECLS #endif From 6e299065ce031ab52848cfda14e8872c16f62fa4 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 17:51:40 +0200 Subject: [PATCH 244/517] Remove RARCH_MENU_CTL_PLAYLIST_ calls - use playlist_init_cached, playlist_get_cached, and playlist_free_cached from now on (defined in playlist.h) - no more menu dependencies on cached playlists --- command.c | 8 +---- menu/cbs/menu_cbs_ok.c | 77 ++++++++++++++++++----------------------- menu/drivers/xmb.c | 2 +- menu/menu_displaylist.c | 61 ++++++++++++++++++-------------- menu/menu_driver.c | 21 +---------- menu/menu_driver.h | 3 -- 6 files changed, 71 insertions(+), 101 deletions(-) diff --git a/command.c b/command.c index 7e8f38ef42..64b4f9f5f4 100644 --- a/command.c +++ b/command.c @@ -1708,14 +1708,8 @@ void command_playlist_update_write( const char *db_name) { playlist_t *plist = (playlist_t*)data; - playlist_t *playlist = NULL; + playlist_t *playlist = plist ? plist : playlist_get_cached(); - if (plist) - playlist = plist; -#ifdef HAVE_MENU - else - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &playlist); -#endif if (!playlist) return; diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index d3625fe160..c561763986 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -1491,9 +1491,8 @@ static int action_ok_playlist_entry_collection(const char *path, if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); - new_core_path[0] = '\0'; - - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &tmp_playlist); + new_core_path[0] = '\0'; + tmp_playlist = playlist_get_cached(); if (!tmp_playlist) { @@ -1540,17 +1539,18 @@ static int action_ok_playlist_entry_collection(const char *path, return ret; } - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &tmp_playlist); + tmp_playlist = playlist_get_cached(); - command_playlist_update_write( - tmp_playlist, - selection_ptr, - NULL, - NULL, - new_core_path, - core_info.inf->display_name, - NULL, - NULL); + if (tmp_playlist) + command_playlist_update_write( + tmp_playlist, + selection_ptr, + NULL, + NULL, + new_core_path, + core_info.inf->display_name, + NULL, + NULL); } else strlcpy(new_core_path, core_path, sizeof(new_core_path)); @@ -1656,14 +1656,12 @@ static int action_ok_playlist_entry_start_content(const char *path, const char *entry_label = NULL; const char *core_path = NULL; const char *core_name = NULL; - playlist_t *tmp_playlist = NULL; menu_handle_t *menu = NULL; + playlist_t *tmp_playlist = playlist_get_cached(); if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &tmp_playlist); - if (!tmp_playlist) { tmp_playlist = playlist_init( @@ -1713,18 +1711,18 @@ static int action_ok_playlist_entry_start_content(const char *path, return ret; } - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &tmp_playlist); + tmp_playlist = playlist_get_cached(); - command_playlist_update_write( - tmp_playlist, - selection_ptr, - NULL, - NULL, - new_core_path, - core_info.inf->display_name, - NULL, - NULL); - + if (tmp_playlist) + command_playlist_update_write( + tmp_playlist, + selection_ptr, + NULL, + NULL, + new_core_path, + core_info.inf->display_name, + NULL, + NULL); } if (!playlist || !menu_content_playlist_load(playlist, selection_ptr)) @@ -1754,8 +1752,7 @@ static int action_ok_audio_add_to_mixer(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *entry_path = NULL; - playlist_t *tmp_playlist = NULL; - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &tmp_playlist); + playlist_t *tmp_playlist = playlist_get_cached(); if (!tmp_playlist) return -1; @@ -2986,16 +2983,13 @@ static int action_ok_reset_core_association(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *tmp_path = NULL; - playlist_t *tmp_playlist = NULL; menu_handle_t *menu = NULL; - - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &tmp_playlist); + playlist_t *tmp_playlist = playlist_get_cached(); if (!tmp_playlist) return 0; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); playlist_get_index(tmp_playlist, menu->rpl_entry_selection_ptr, @@ -3020,16 +3014,13 @@ static int action_ok_add_to_favorites_playlist(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *tmp_path = NULL; - playlist_t *tmp_playlist = NULL; menu_handle_t *menu = NULL; - - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &tmp_playlist); + playlist_t *tmp_playlist = playlist_get_cached(); if (!tmp_playlist) return 0; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); playlist_get_index(tmp_playlist, menu->rpl_entry_selection_ptr, &tmp_path, @@ -3045,7 +3036,6 @@ static int action_ok_delete_entry(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { size_t new_selection_ptr; - playlist_t *playlist = NULL; char *conf_path = NULL; char *def_conf_path = NULL; char *def_conf_music_path = NULL; @@ -3056,12 +3046,11 @@ static int action_ok_delete_entry(const char *path, char *def_conf_img_path = NULL; #endif menu_handle_t *menu = NULL; + playlist_t *playlist = playlist_get_cached(); if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &playlist); - conf_path = playlist_get_conf_path(playlist); def_conf_path = playlist_get_conf_path(g_defaults.content_history); def_conf_music_path = playlist_get_conf_path(g_defaults.music_history); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 4c979fe0e0..53ca07b3d7 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -1019,7 +1019,7 @@ static void xmb_update_thumbnail_path(void *data, unsigned i, char pos) goto end; } - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &playlist); + playlist = playlist_get_cached(); if (playlist) { diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 043fcb9b6e..e370a774de 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -2486,13 +2486,16 @@ static int menu_displaylist_parse_settings_enum(void *data, static void menu_displaylist_set_new_playlist( menu_handle_t *menu, const char *path) { - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_FREE, NULL); - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_INIT, - (void*)path); - strlcpy( - menu->db_playlist_file, - path, - sizeof(menu->db_playlist_file)); + menu->db_playlist_file[0] = '\0'; + + if (playlist_get_cached()) + playlist_free_cached(); + + if (playlist_init_cached(path, COLLECTION_SIZE)) + strlcpy( + menu->db_playlist_file, + path, + sizeof(menu->db_playlist_file)); } @@ -2545,12 +2548,15 @@ static int menu_displaylist_parse_horizontal_list( menu_displaylist_set_new_playlist(menu, path_playlist); } - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &playlist); + playlist = playlist_get_cached(); - playlist_qsort(playlist); - - menu_displaylist_parse_playlist(info, - playlist, msg_hash_to_str(MENU_ENUM_LABEL_COLLECTION), is_historylist); + if (playlist) + { + playlist_qsort(playlist); + menu_displaylist_parse_playlist(info, + playlist, + msg_hash_to_str(MENU_ENUM_LABEL_COLLECTION), is_historylist); + } return 0; } @@ -2774,7 +2780,7 @@ static int menu_displaylist_parse_horizontal_content_actions( const char *core_path = NULL; const char *core_name = NULL; const char *db_name = NULL; - playlist_t *playlist = NULL; + playlist_t *playlist = playlist_get_cached(); settings_t *settings = config_get_ptr(); const char *fullpath = path_get(RARCH_PATH_CONTENT); @@ -2783,10 +2789,9 @@ static int menu_displaylist_parse_horizontal_content_actions( idx = menu->rpl_entry_selection_ptr; - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &playlist); - - playlist_get_index(playlist, idx, - &entry_path, &label, &core_path, &core_name, NULL, &db_name); + if (playlist) + playlist_get_index(playlist, idx, + &entry_path, &label, &core_path, &core_name, NULL, &db_name); content_loaded = !rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL) && string_is_equal(menu->deferred_path, fullpath); @@ -2815,14 +2820,16 @@ static int menu_displaylist_parse_horizontal_content_actions( msg_hash_to_str(MENU_ENUM_LABEL_RUN), MENU_ENUM_LABEL_RUN, FILE_TYPE_PLAYLIST_ENTRY, 0, idx); - if (settings->bools.playlist_entry_rename && !settings->bools.kiosk_mode_enable) + if (settings->bools.playlist_entry_rename && + !settings->bools.kiosk_mode_enable) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RENAME_ENTRY), msg_hash_to_str(MENU_ENUM_LABEL_RENAME_ENTRY), MENU_ENUM_LABEL_RENAME_ENTRY, FILE_TYPE_PLAYLIST_ENTRY, 0, idx); - if (settings->bools.playlist_entry_remove && !settings->bools.kiosk_mode_enable) + if (settings->bools.playlist_entry_remove && + !settings->bools.kiosk_mode_enable) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DELETE_ENTRY), msg_hash_to_str(MENU_ENUM_LABEL_DELETE_ENTRY), @@ -3983,9 +3990,8 @@ static void menu_displaylist_parse_playlist_generic( menu_displaylist_set_new_playlist(menu, playlist_path); - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &playlist); - - path_playlist = strdup(playlist_name); + playlist = playlist_get_cached(); + path_playlist = strdup(playlist_name); *ret = menu_displaylist_parse_playlist(info, playlist, path_playlist, true); @@ -4414,12 +4420,15 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) msg_hash_to_str(MENU_ENUM_LABEL_COLLECTION), sizeof(path_playlist)); - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &playlist); + playlist = playlist_get_cached(); - playlist_qsort(playlist); + if (playlist) + { + playlist_qsort(playlist); - ret = menu_displaylist_parse_playlist(info, - playlist, path_playlist, false); + ret = menu_displaylist_parse_playlist(info, + playlist, path_playlist, false); + } if (ret == 0) { diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 403ce0d617..ddbadabe02 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -1877,9 +1877,6 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) case RARCH_MENU_CTL_SET_PENDING_SHUTDOWN: menu_driver_pending_shutdown = true; break; - case RARCH_MENU_CTL_PLAYLIST_FREE: - playlist_free_cached(); - break; case RARCH_MENU_CTL_FIND_DRIVER: { int i; @@ -1920,22 +1917,6 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) } } break; - case RARCH_MENU_CTL_PLAYLIST_INIT: - { - const char *path = (const char*)data; - if (string_is_empty(path)) - return false; - playlist_init_cached(path, COLLECTION_SIZE); - } - break; - case RARCH_MENU_CTL_PLAYLIST_GET: - { - playlist_t **playlist = (playlist_t**)data; - if (!playlist) - return false; - *playlist = playlist_get_cached(); - } - break; case RARCH_MENU_CTL_SET_PREVENT_POPULATE: menu_driver_prevent_populate = true; break; @@ -1967,7 +1948,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) if (menu_driver_data_own) return true; - menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_FREE, NULL); + playlist_free_cached(); menu_shader_manager_free(); if (menu_driver_data) diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 94195c73d9..412e920478 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -100,9 +100,6 @@ enum rarch_menu_ctl_state RARCH_MENU_CTL_SET_OWN_DRIVER, RARCH_MENU_CTL_UNSET_OWN_DRIVER, RARCH_MENU_CTL_OWNS_DRIVER, - RARCH_MENU_CTL_PLAYLIST_FREE, - RARCH_MENU_CTL_PLAYLIST_INIT, - RARCH_MENU_CTL_PLAYLIST_GET, RARCH_MENU_CTL_FIND_DRIVER, RARCH_MENU_CTL_LIST_FREE, RARCH_MENU_CTL_LIST_SET_SELECTION, From d2a1c39f4b4e4aa874dcf21fa0f4272a7cf73662 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 18:39:03 +0200 Subject: [PATCH 245/517] (menu) Start passing menu_handle to action_ok callback --- menu/cbs/menu_cbs_ok.c | 455 +++++++++++++++++++++---------------- menu/cbs/menu_cbs_select.c | 6 +- menu/menu_cbs.h | 23 +- menu/menu_driver.c | 3 +- menu/menu_entries.h | 3 +- menu/widgets/menu_entry.c | 12 +- 6 files changed, 297 insertions(+), 205 deletions(-) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index c561763986..c053fbb285 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -7,7 +7,7 @@ * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * @@ -96,7 +96,7 @@ enum #ifndef BIND_ACTION_OK #define BIND_ACTION_OK(cbs, name) \ do { \ - cbs->action_ok = name; \ + cbs->action_ok = name; \ cbs->action_ok_ident = #name; \ } while(0) #endif @@ -309,7 +309,9 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MSG_UNKNOWN; } -int generic_action_ok_displaylist_push(const char *path, +int generic_action_ok_displaylist_push( + void *data, + const char *path, const char *new_path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned action_type) @@ -323,7 +325,7 @@ int generic_action_ok_displaylist_push(const char *path, const char *content_path = NULL; const char *info_label = NULL; const char *info_path = NULL; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; enum msg_hash_enums enum_idx = MSG_UNKNOWN; settings_t *settings = config_get_ptr(); file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); @@ -332,7 +334,7 @@ int generic_action_ok_displaylist_push(const char *path, info.list = menu_stack; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) goto end; tmp[0] = '\0'; @@ -990,6 +992,7 @@ static void content_add_to_playlist(const char *path) } static int file_load_with_detect_core_wrapper( + void *data, enum msg_hash_enums enum_label_idx, enum msg_hash_enums enum_idx, size_t idx, size_t entry_idx, @@ -1001,10 +1004,10 @@ static int file_load_with_detect_core_wrapper( char *new_core_path = NULL; const char *menu_path = NULL; const char *menu_label = NULL; - menu_handle_t *menu = NULL; core_info_list_t *list = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); { @@ -1051,7 +1054,7 @@ static int file_load_with_detect_core_wrapper( if (enum_label_idx == MENU_ENUM_LABEL_COLLECTION) { free(new_core_path); - return generic_action_ok_displaylist_push(path, NULL, + return generic_action_ok_displaylist_push(menu, path, NULL, NULL, 0, idx, entry_idx, ACTION_OK_DL_DEFERRED_CORE_LIST_SET); } @@ -1081,7 +1084,8 @@ static int file_load_with_detect_core_wrapper( break; } case 0: - ret = generic_action_ok_displaylist_push(path, NULL, label, type, + ret = generic_action_ok_displaylist_push(menu, + path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_DEFERRED_CORE_LIST); break; default: @@ -1094,12 +1098,13 @@ static int file_load_with_detect_core_wrapper( } static int action_ok_file_load_with_detect_core_carchive( + void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); fill_pathname_join_delim(menu->detect_content_path, @@ -1109,12 +1114,14 @@ static int action_ok_file_load_with_detect_core_carchive( type = 0; label = NULL; - return file_load_with_detect_core_wrapper(MSG_UNKNOWN, + return file_load_with_detect_core_wrapper(menu, + MSG_UNKNOWN, MSG_UNKNOWN, idx, entry_idx, path, label, type, true); } -static int action_ok_file_load_with_detect_core(const char *path, +static int action_ok_file_load_with_detect_core(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { @@ -1122,18 +1129,21 @@ static int action_ok_file_load_with_detect_core(const char *path, label = NULL; return file_load_with_detect_core_wrapper( + data, MSG_UNKNOWN, MSG_UNKNOWN, idx, entry_idx, path, label, type, false); } -static int action_ok_file_load_with_detect_core_collection(const char *path, +static int action_ok_file_load_with_detect_core_collection(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { type = 0; label = NULL; return file_load_with_detect_core_wrapper( + data, MENU_ENUM_LABEL_COLLECTION, MSG_UNKNOWN, idx, entry_idx, path, label, type, false); @@ -1160,7 +1170,8 @@ static int generic_action_ok_command(enum event_command cmd) return 0; } -static int generic_action_ok(const char *path, +static int generic_action_ok(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned id, enum msg_hash_enums flush_id) { @@ -1171,9 +1182,9 @@ static int generic_action_ok(const char *path, const char *menu_path = NULL; const char *menu_label = NULL; const char *flush_char = NULL; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) goto error; menu_entries_get_last_stack(&menu_path, @@ -1334,7 +1345,8 @@ error: return menu_cbs_exit(); } -static int default_action_ok_load_content_with_core_from_menu(const char *_path, unsigned _type) +static int default_action_ok_load_content_with_core_from_menu( + const char *_path, unsigned _type) { content_ctx_info_t content_info; content_info.argc = 0; @@ -1349,7 +1361,8 @@ static int default_action_ok_load_content_with_core_from_menu(const char *_path, return 0; } -static int default_action_ok_load_content_from_playlist_from_menu(const char *_path, +static int default_action_ok_load_content_from_playlist_from_menu( + const char *_path, const char *path, const char *entry_label) { content_ctx_info_t content_info; @@ -1367,9 +1380,9 @@ static int default_action_ok_load_content_from_playlist_from_menu(const char *_p #define default_action_ok_set(funcname, _id, _flush) \ -static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return generic_action_ok(path, label, type, idx, entry_idx, _id, _flush); \ + return generic_action_ok(data, path, label, type, idx, entry_idx, _id, _flush); \ } default_action_ok_set(action_ok_set_path_audiofilter, ACTION_OK_SET_PATH_VIDEO_FILTER, MSG_UNKNOWN) @@ -1386,7 +1399,8 @@ default_action_ok_set(action_ok_remap_file_load, ACTION_OK_LOAD_REMAPPING_F default_action_ok_set(action_ok_shader_preset_load, ACTION_OK_LOAD_PRESET , MENU_ENUM_LABEL_SHADER_OPTIONS) default_action_ok_set(action_ok_shader_pass_load, ACTION_OK_LOAD_SHADER_PASS, MENU_ENUM_LABEL_SHADER_OPTIONS) -static int action_ok_file_load(const char *path, +static int action_ok_file_load(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char menu_path_new[PATH_MAX_LENGTH]; @@ -1395,17 +1409,18 @@ static int action_ok_file_load(const char *path, const char *menu_path = NULL; rarch_setting_t *setting = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + menu_handle_t *menu = (menu_handle_t*)data; menu_path_new[0] = full_path_new[0] = '\0'; + if (!menu) + return menu_cbs_exit(); + if (filebrowser_get_type() == FILEBROWSER_SELECT_FILE_SUBSYSTEM) { /* TODO/FIXME - this path is triggered when we try to load a * file from an archive while inside the load subsystem * action */ - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); fill_pathname_join(menu_path_new, menu->scratch2_buf, menu->scratch_buf, @@ -1433,7 +1448,7 @@ static int action_ok_file_load(const char *path, setting = menu_setting_find(menu_label); if (setting_get_type(setting) == ST_PATH) - return action_ok_set_path(path, label, type, idx, entry_idx); + return action_ok_set_path(data, path, label, type, idx, entry_idx); if (!string_is_empty(menu_path)) strlcpy(menu_path_new, menu_path, sizeof(menu_path_new)); @@ -1446,15 +1461,9 @@ static int action_ok_file_load(const char *path, string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_ARCHIVE_OPEN)) ) - { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - fill_pathname_join(menu_path_new, menu->scratch2_buf, menu->scratch_buf, sizeof(menu_path_new)); - } } switch (type) @@ -1474,7 +1483,8 @@ static int action_ok_file_load(const char *path, } -static int action_ok_playlist_entry_collection(const char *path, +static int action_ok_playlist_entry_collection(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char new_core_path[PATH_MAX_LENGTH]; @@ -1486,9 +1496,9 @@ static int action_ok_playlist_entry_collection(const char *path, const char *core_path = NULL; const char *core_name = NULL; playlist_t *tmp_playlist = NULL; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); new_core_path[0] = '\0'; @@ -1532,7 +1542,8 @@ static int action_ok_playlist_entry_collection(const char *path, { /* TODO: figure out if this should refer to the inner or outer entry_path */ /* TODO: make sure there's only one entry_path in this function */ - int ret = action_ok_file_load_with_detect_core_collection(entry_path, + int ret = action_ok_file_load_with_detect_core_collection(menu, + entry_path, label, type, selection_ptr, entry_idx); if (playlist_initialized) playlist_free(tmp_playlist); @@ -1572,7 +1583,8 @@ static int action_ok_playlist_entry_collection(const char *path, new_core_path, path, entry_label); } -static int action_ok_playlist_entry(const char *path, +static int action_ok_playlist_entry(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char new_core_path[PATH_MAX_LENGTH]; @@ -1582,11 +1594,11 @@ static int action_ok_playlist_entry(const char *path, const char *entry_label = NULL; const char *core_path = NULL; const char *core_name = NULL; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; new_core_path[0] = '\0'; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); selection_ptr = entry_idx; @@ -1614,7 +1626,8 @@ static int action_ok_playlist_entry(const char *path, if (!found_associated_core) /* TODO: figure out if this should refer to the inner or outer entry_path */ /* TODO: make sure there's only one entry_path in this function */ - return action_ok_file_load_with_detect_core(entry_path, + return action_ok_file_load_with_detect_core(menu, + entry_path, label, type, selection_ptr, entry_idx); command_playlist_update_write(NULL, @@ -1646,7 +1659,8 @@ static int action_ok_playlist_entry(const char *path, new_core_path, path, entry_label); } -static int action_ok_playlist_entry_start_content(const char *path, +static int action_ok_playlist_entry_start_content(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { size_t selection_ptr = 0; @@ -1656,10 +1670,10 @@ static int action_ok_playlist_entry_start_content(const char *path, const char *entry_label = NULL; const char *core_path = NULL; const char *core_name = NULL; - menu_handle_t *menu = NULL; playlist_t *tmp_playlist = playlist_get_cached(); + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); if (!tmp_playlist) @@ -1704,7 +1718,8 @@ static int action_ok_playlist_entry_start_content(const char *path, { /* TODO: figure out if this should refer to the inner or outer entry_path */ /* TODO: make sure there's only one entry_path in this function */ - int ret = action_ok_file_load_with_detect_core(entry_path, + int ret = action_ok_file_load_with_detect_core(menu, + entry_path, label, type, selection_ptr, entry_idx); if (playlist_initialized) playlist_free(tmp_playlist); @@ -1742,13 +1757,15 @@ error: return menu_cbs_exit(); } -static int action_ok_lookup_setting(const char *path, +static int action_ok_lookup_setting(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return menu_setting_set(type, label, MENU_ACTION_OK, false); } -static int action_ok_audio_add_to_mixer(const char *path, +static int action_ok_audio_add_to_mixer(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *entry_path = NULL; @@ -1767,15 +1784,16 @@ static int action_ok_audio_add_to_mixer(const char *path, return 0; } -static int action_ok_audio_add_to_mixer_and_collection(const char *path, +static int action_ok_audio_add_to_mixer_and_collection(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char combined_path[PATH_MAX_LENGTH]; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; combined_path[0] = '\0'; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); fill_pathname_join(combined_path, menu->scratch2_buf, @@ -1795,21 +1813,24 @@ static int action_ok_audio_add_to_mixer_and_collection(const char *path, return 0; } -static int action_ok_menu_wallpaper(const char *path, +static int action_ok_menu_wallpaper(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { filebrowser_set_type(FILEBROWSER_SELECT_IMAGE); - return action_ok_lookup_setting(path, label, type, idx, entry_idx); + return action_ok_lookup_setting(data, path, label, type, idx, entry_idx); } -static int action_ok_menu_font(const char *path, +static int action_ok_menu_font(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { filebrowser_set_type(FILEBROWSER_SELECT_FONT); - return action_ok_lookup_setting(path, label, type, idx, entry_idx); + return action_ok_lookup_setting(data, path, label, type, idx, entry_idx); } -static int action_ok_menu_wallpaper_load(const char *path, +static int action_ok_menu_wallpaper_load(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { settings_t *settings = config_get_ptr(); @@ -1817,17 +1838,18 @@ static int action_ok_menu_wallpaper_load(const char *path, filebrowser_clear_type(); settings->uints.menu_xmb_shader_pipeline = XMB_SHADER_PIPELINE_WALLPAPER; - return generic_action_ok(path, label, type, idx, entry_idx, + return generic_action_ok(data, path, label, type, idx, entry_idx, ACTION_OK_LOAD_WALLPAPER, MSG_UNKNOWN); } -int generic_action_ok_help(const char *path, +int generic_action_ok_help(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums id, enum menu_dialog_type id2) { const char *lbl = msg_hash_to_str(id); - return generic_action_ok_displaylist_push(path, NULL, lbl, id2, idx, + return generic_action_ok_displaylist_push(data, path, NULL, lbl, id2, idx, entry_idx, ACTION_OK_DL_HELP); } @@ -1990,7 +2012,7 @@ static void menu_input_st_string_cb_cheat_file_save_as( } #define default_action_dialog_start(funcname, _label_setting, _idx, _cb) \ -static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ menu_input_ctx_line_t line; \ line.label = label; \ @@ -2039,7 +2061,8 @@ enum ACTION_OK_SHADER_PRESET_SAVE_PARENT }; -static int generic_action_ok_shader_preset_save(const char *path, +static int generic_action_ok_shader_preset_save(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned action_type) { @@ -2103,28 +2126,32 @@ static int generic_action_ok_shader_preset_save(const char *path, return 0; } -static int action_ok_shader_preset_save_core(const char *path, +static int action_ok_shader_preset_save_core(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_shader_preset_save(path, label, type, + return generic_action_ok_shader_preset_save(data, path, label, type, idx, entry_idx, ACTION_OK_SHADER_PRESET_SAVE_CORE); } -static int action_ok_shader_preset_save_game(const char *path, +static int action_ok_shader_preset_save_game(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_shader_preset_save(path, label, type, + return generic_action_ok_shader_preset_save(data, path, label, type, idx, entry_idx, ACTION_OK_SHADER_PRESET_SAVE_GAME); } -static int action_ok_shader_preset_save_parent(const char *path, +static int action_ok_shader_preset_save_parent(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_shader_preset_save(path, label, type, + return generic_action_ok_shader_preset_save(data, path, label, type, idx, entry_idx, ACTION_OK_SHADER_PRESET_SAVE_PARENT); } -static int generic_action_ok_remap_file_operation(const char *path, +static int generic_action_ok_remap_file_operation(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned action_type) { @@ -2212,68 +2239,76 @@ static int generic_action_ok_remap_file_operation(const char *path, return 0; } -static int action_ok_remap_file_save_core(const char *path, +static int action_ok_remap_file_save_core(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_remap_file_operation(path, label, type, + return generic_action_ok_remap_file_operation(data, path, label, type, idx, entry_idx, ACTION_OK_REMAP_FILE_SAVE_CORE); } -static int action_ok_remap_file_save_game(const char *path, +static int action_ok_remap_file_save_game(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_remap_file_operation(path, label, type, + return generic_action_ok_remap_file_operation(data, path, label, type, idx, entry_idx, ACTION_OK_REMAP_FILE_SAVE_GAME); } -static int action_ok_remap_file_remove_core(const char *path, +static int action_ok_remap_file_remove_core(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_remap_file_operation(path, label, type, + return generic_action_ok_remap_file_operation(data, path, label, type, idx, entry_idx, ACTION_OK_REMAP_FILE_REMOVE_CORE); } -static int action_ok_remap_file_remove_game(const char *path, +static int action_ok_remap_file_remove_game(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_remap_file_operation(path, label, type, + return generic_action_ok_remap_file_operation(data, path, label, type, idx, entry_idx, ACTION_OK_REMAP_FILE_REMOVE_GAME); } -int action_ok_path_use_directory(const char *path, +int action_ok_path_use_directory(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { filebrowser_clear_type(); - return generic_action_ok(NULL, label, type, idx, entry_idx, + return generic_action_ok(data, NULL, label, type, idx, entry_idx, ACTION_OK_SET_DIRECTORY, MSG_UNKNOWN); } #ifdef HAVE_LIBRETRODB -static int action_ok_scan_file(const char *path, +static int action_ok_scan_file(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return action_scan_file(path, label, type, idx); } -static int action_ok_path_scan_directory(const char *path, +static int action_ok_path_scan_directory(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return action_scan_directory(NULL, label, type, idx); } #endif -static int action_ok_core_deferred_set(const char *new_core_path, +static int action_ok_core_deferred_set(void *data, + const char *new_core_path, const char *content_label, unsigned type, size_t idx, size_t entry_idx) { char ext_name[255]; char core_display_name[PATH_MAX_LENGTH]; settings_t *settings = config_get_ptr(); - menu_handle_t *menu = NULL; size_t selection = menu_navigation_get_selection(); + menu_handle_t *menu = (menu_handle_t*)data; ext_name[0] = '\0'; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); if (!frontend_driver_get_core_extension(ext_name, sizeof(ext_name))) @@ -2303,24 +2338,26 @@ static int action_ok_core_deferred_set(const char *new_core_path, return menu_cbs_exit(); } -static int action_ok_deferred_list_stub(const char *path, +static int action_ok_deferred_list_stub(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return 0; } -static int action_ok_load_core_deferred(const char *path, +static int action_ok_load_core_deferred(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { content_ctx_info_t content_info; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; content_info.argc = 0; content_info.argv = NULL; content_info.args = NULL; content_info.environ_get = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); if (!task_push_load_content_with_new_core_from_menu( @@ -2335,7 +2372,7 @@ static int action_ok_load_core_deferred(const char *path, } #define default_action_ok_start_builtin_core(funcname, _id) \ -static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ content_ctx_info_t content_info; \ content_info.argc = 0; \ @@ -2351,7 +2388,7 @@ default_action_ok_start_builtin_core(action_ok_start_net_retropad_core, CORE_TYP default_action_ok_start_builtin_core(action_ok_start_video_processor_core, CORE_TYPE_VIDEO_PROCESSOR) #ifdef HAVE_FFMPEG -static int action_ok_file_load_ffmpeg(const char *path, +static int action_ok_file_load_ffmpeg(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char new_path[PATH_MAX_LENGTH]; @@ -2370,15 +2407,15 @@ static int action_ok_file_load_ffmpeg(const char *path, } #endif -static int action_ok_audio_run(const char *path, +static int action_ok_audio_run(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char combined_path[PATH_MAX_LENGTH]; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; combined_path[0] = '\0'; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); fill_pathname_join(combined_path, menu->scratch2_buf, @@ -2387,7 +2424,8 @@ static int action_ok_audio_run(const char *path, return default_action_ok_load_content_with_core_from_menu(combined_path, CORE_TYPE_FFMPEG); } -static int action_ok_file_load_imageviewer(const char *path, +static int action_ok_file_load_imageviewer(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char fullpath[PATH_MAX_LENGTH]; @@ -2405,25 +2443,27 @@ static int action_ok_file_load_imageviewer(const char *path, return default_action_ok_load_content_with_core_from_menu(fullpath, CORE_TYPE_IMAGEVIEWER); } -static int action_ok_file_load_current_core(const char *path, +static int action_ok_file_load_current_core(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!data) return menu_cbs_exit(); return default_action_ok_load_content_with_core_from_menu( menu->detect_content_path, CORE_TYPE_PLAIN); } -static int action_ok_file_load_detect_core(const char *path, +static int action_ok_file_load_detect_core(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { content_ctx_info_t content_info; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); content_info.argc = 0; @@ -2442,8 +2482,7 @@ static int action_ok_file_load_detect_core(const char *path, return 0; } - -static int action_ok_load_state(const char *path, +static int action_ok_load_state(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { if (generic_action_ok_command(CMD_EVENT_LOAD_STATE) == -1) @@ -2451,7 +2490,8 @@ static int action_ok_load_state(const char *path, return generic_action_ok_command(CMD_EVENT_RESUME); } -static int action_ok_save_state(const char *path, +static int action_ok_save_state(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { if (generic_action_ok_command(CMD_EVENT_SAVE_STATE) == -1) @@ -2459,7 +2499,8 @@ static int action_ok_save_state(const char *path, return generic_action_ok_command(CMD_EVENT_RESUME); } -static int action_ok_undo_load_state(const char *path, +static int action_ok_undo_load_state(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { if (generic_action_ok_command(CMD_EVENT_UNDO_LOAD_STATE) == -1) @@ -2467,7 +2508,8 @@ static int action_ok_undo_load_state(const char *path, return generic_action_ok_command(CMD_EVENT_RESUME); } -static int action_ok_undo_save_state(const char *path, +static int action_ok_undo_save_state(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { if (generic_action_ok_command(CMD_EVENT_UNDO_SAVE_STATE) == -1) @@ -2511,7 +2553,9 @@ static void cb_decompressed(void *task_data, void *user_data, const char *err) } #endif -static int generic_action_ok_network(const char *path, +static int generic_action_ok_network( + void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums enum_idx) { @@ -2595,14 +2639,14 @@ static int generic_action_ok_network(const char *path, task_push_http_transfer(url_path, suppress_msg, url_label, callback, transf); - return generic_action_ok_displaylist_push(path, NULL, + return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, entry_idx, type_id2); } #define default_action_ok_list(funcname, _id) \ -static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return generic_action_ok_network(path, label, type, idx, entry_idx, _id); \ + return generic_action_ok_network(data, path, label, type, idx, entry_idx, _id); \ } default_action_ok_list(action_ok_core_content_list, MENU_ENUM_LABEL_CB_CORE_CONTENT_LIST) @@ -2618,8 +2662,12 @@ static void cb_generic_dir_download(void *task_data, if (transf) { - generic_action_ok_network(transf->path, transf->path, 0, 0, 0, - MENU_ENUM_LABEL_CB_CORE_CONTENT_LIST); + menu_handle_t *menu = NULL; + + if (menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + generic_action_ok_network(menu, + transf->path, transf->path, 0, 0, 0, + MENU_ENUM_LABEL_CB_CORE_CONTENT_LIST); free(transf); } @@ -2792,7 +2840,8 @@ finish: #endif -static int action_ok_download_generic(const char *path, +static int action_ok_download_generic(void *data, + const char *path, const char *label, const char *menu_label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums enum_idx) @@ -2883,7 +2932,8 @@ static int action_ok_download_generic(const char *path, return 0; } -static int action_ok_core_content_download(const char *path, +static int action_ok_core_content_download(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *menu_path = NULL; @@ -2892,15 +2942,15 @@ static int action_ok_core_content_download(const char *path, menu_entries_get_last_stack(&menu_path, &menu_label, NULL, &enum_idx, NULL); - return action_ok_download_generic(path, label, + return action_ok_download_generic(data, path, label, menu_path, type, idx, entry_idx, MENU_ENUM_LABEL_CB_CORE_CONTENT_DOWNLOAD); } #define default_action_ok_download(funcname, _id) \ -static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return action_ok_download_generic(path, label, NULL, type, idx, entry_idx,_id); \ + return action_ok_download_generic(data, path, label, NULL, type, idx, entry_idx,_id); \ } default_action_ok_download(action_ok_core_content_thumbnails, MENU_ENUM_LABEL_CB_CORE_THUMBNAILS_DOWNLOAD) @@ -2919,7 +2969,8 @@ default_action_ok_download(action_ok_update_cheats, MENU_ENUM_LABEL_CB_UPDATE_CH default_action_ok_download(action_ok_update_autoconfig_profiles, MENU_ENUM_LABEL_CB_UPDATE_AUTOCONFIG_PROFILES) /* creates folder and core options stub file for subsequent runs */ -static int action_ok_option_create(const char *path, +static int action_ok_option_create(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char game_path[PATH_MAX_LENGTH]; @@ -2956,7 +3007,9 @@ static int action_ok_option_create(const char *path, return 0; } -int action_ok_close_content(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) +int action_ok_close_content(void *data, + const char *path, const char *label, + unsigned type, size_t idx, size_t entry_idx) { /* This line resets the navigation pointer so the active entry will be "Run" */ menu_navigation_set_selection(0); @@ -2964,7 +3017,7 @@ int action_ok_close_content(const char *path, const char *label, unsigned type, } #define default_action_ok_cmd_func(func_name, cmd) \ -int (func_name)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +int (func_name)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ return generic_action_ok_command(cmd); \ } @@ -2979,16 +3032,17 @@ default_action_ok_cmd_func(action_ok_disk_cycle_tray_status, CMD_EVENT_DISK_EJEC default_action_ok_cmd_func(action_ok_shader_apply_changes, CMD_EVENT_SHADERS_APPLY_CHANGES ) -static int action_ok_reset_core_association(const char *path, +static int action_ok_reset_core_association(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *tmp_path = NULL; - menu_handle_t *menu = NULL; playlist_t *tmp_playlist = playlist_get_cached(); + menu_handle_t *menu = (menu_handle_t*)data; if (!tmp_playlist) return 0; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); playlist_get_index(tmp_playlist, @@ -3001,7 +3055,8 @@ static int action_ok_reset_core_association(const char *path, return 0; } -static int action_ok_add_to_favorites(const char *path, +static int action_ok_add_to_favorites(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { void *new_path = (void*)path_get(RARCH_PATH_CONTENT); @@ -3010,16 +3065,17 @@ static int action_ok_add_to_favorites(const char *path, return 0; } -static int action_ok_add_to_favorites_playlist(const char *path, +static int action_ok_add_to_favorites_playlist(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *tmp_path = NULL; - menu_handle_t *menu = NULL; playlist_t *tmp_playlist = playlist_get_cached(); + menu_handle_t *menu = (menu_handle_t*)data; if (!tmp_playlist) return 0; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); playlist_get_index(tmp_playlist, @@ -3032,7 +3088,8 @@ static int action_ok_add_to_favorites_playlist(const char *path, } -static int action_ok_delete_entry(const char *path, +static int action_ok_delete_entry(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { size_t new_selection_ptr; @@ -3045,10 +3102,10 @@ static int action_ok_delete_entry(const char *path, #ifdef HAVE_IMAGEVIEWER char *def_conf_img_path = NULL; #endif - menu_handle_t *menu = NULL; playlist_t *playlist = playlist_get_cached(); + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); conf_path = playlist_get_conf_path(playlist); @@ -3088,7 +3145,8 @@ static int action_ok_delete_entry(const char *path, } -static int action_ok_rdb_entry_submenu(const char *path, +static int action_ok_rdb_entry_submenu(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { union string_list_elem_attr attr; @@ -3140,7 +3198,7 @@ static int action_ok_rdb_entry_submenu(const char *path, str_list->elems[0].data, '_', sizeof(new_label)); - ret = generic_action_ok_displaylist_push(new_path, NULL, + ret = generic_action_ok_displaylist_push(data, new_path, NULL, new_label, type, idx, entry_idx, ACTION_OK_DL_RDB_ENTRY_SUBMENU); @@ -3156,9 +3214,9 @@ end: } #define default_action_ok_func(func_name, lbl) \ -int (func_name)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +int (func_name)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return generic_action_ok_displaylist_push(path, NULL, label, type, idx, entry_idx, lbl); \ + return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, entry_idx, lbl); \ } default_action_ok_func(action_ok_browse_url_start, ACTION_OK_DL_BROWSE_URL_START) @@ -3225,20 +3283,22 @@ default_action_ok_func(action_ok_push_user_binds_list, ACTION_OK_DL_USER_BINDS_L default_action_ok_func(action_ok_push_accounts_cheevos_list, ACTION_OK_DL_ACCOUNTS_CHEEVOS_LIST) default_action_ok_func(action_ok_open_archive, ACTION_OK_DL_OPEN_ARCHIVE) -static int action_ok_shader_pass(const char *path, +static int action_ok_shader_pass(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); menu->hack_shader_pass = type - MENU_SETTINGS_SHADER_PASS_0; - return generic_action_ok_displaylist_push(path, NULL, label, type, idx, + return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_SHADER_PASS); } -static int action_ok_netplay_connect_room(const char *path, +static int action_ok_netplay_connect_room(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -3285,8 +3345,8 @@ static int action_ok_netplay_connect_room(const char *path, return 0; } - -static int action_ok_netplay_lan_scan(const char *path, +static int action_ok_netplay_lan_scan(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -3313,12 +3373,12 @@ static int action_ok_netplay_lan_scan(const char *path, } #define default_action_ok_dl_push(funcname, _fbid, _id, _path) \ -static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ settings_t *settings = config_get_ptr(); \ (void)settings; \ filebrowser_set_type(_fbid); \ - return generic_action_ok_displaylist_push(path, _path, label, type, idx, entry_idx, _id); \ + return generic_action_ok_displaylist_push(data, path, _path, label, type, idx, entry_idx, _id); \ } default_action_ok_dl_push(action_ok_content_collection_list, FILEBROWSER_SELECT_COLLECTION, ACTION_OK_DL_CONTENT_COLLECTION_LIST, NULL) @@ -3587,7 +3647,8 @@ static void netplay_lan_scan_callback(void *task_data, } } -static int action_ok_push_netplay_refresh_rooms(const char *path, +static int action_ok_push_netplay_refresh_rooms(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char url [2048] = "http://newlobby.libretro.com/list/"; @@ -3600,76 +3661,83 @@ static int action_ok_push_netplay_refresh_rooms(const char *path, #endif -static int action_ok_scan_directory_list(const char *path, +static int action_ok_scan_directory_list(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { settings_t *settings = config_get_ptr(); filebrowser_clear_type(); - return generic_action_ok_displaylist_push(path, + return generic_action_ok_displaylist_push(data, path, settings->paths.directory_menu_content, label, type, idx, entry_idx, ACTION_OK_DL_SCAN_DIR_LIST); } -static int action_ok_push_random_dir(const char *path, +static int action_ok_push_random_dir(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_displaylist_push(path, path, + return generic_action_ok_displaylist_push(data, path, path, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES), type, idx, entry_idx, ACTION_OK_DL_CONTENT_LIST); } -static int action_ok_push_downloads_dir(const char *path, +static int action_ok_push_downloads_dir(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { settings_t *settings = config_get_ptr(); filebrowser_set_type(FILEBROWSER_SELECT_FILE); - return generic_action_ok_displaylist_push(path, + return generic_action_ok_displaylist_push(data, path, settings->paths.directory_core_assets, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES), type, idx, entry_idx, ACTION_OK_DL_CONTENT_LIST); } -int action_ok_push_filebrowser_list_dir_select(const char *path, +int action_ok_push_filebrowser_list_dir_select(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); filebrowser_set_type(FILEBROWSER_SELECT_DIR); strlcpy(menu->filebrowser_label, label, sizeof(menu->filebrowser_label)); - return generic_action_ok_displaylist_push(path, NULL, label, type, idx, + return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_FILE_BROWSER_SELECT_DIR); } -int action_ok_push_filebrowser_list_file_select(const char *path, +int action_ok_push_filebrowser_list_file_select(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); filebrowser_set_type(FILEBROWSER_SELECT_FILE); strlcpy(menu->filebrowser_label, label, sizeof(menu->filebrowser_label)); - return generic_action_ok_displaylist_push(path, NULL, label, type, idx, + return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_FILE_BROWSER_SELECT_DIR); } -static int action_ok_push_default(const char *path, +static int action_ok_push_default(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { filebrowser_clear_type(); - return generic_action_ok_displaylist_push(path, NULL, label, type, idx, + return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_PUSH_DEFAULT); } -static int action_ok_start_core(const char *path, +static int action_ok_start_core(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { content_ctx_info_t content_info; @@ -3686,18 +3754,19 @@ static int action_ok_start_core(const char *path, return 0; } -static int action_ok_load_archive(const char *path, +static int action_ok_load_archive(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = NULL; - const char *menu_path = NULL; - const char *content_path = NULL; + const char *menu_path = NULL; + const char *content_path = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); - menu_path = menu->scratch2_buf; - content_path = menu->scratch_buf; + menu_path = menu->scratch2_buf; + content_path = menu->scratch_buf; fill_pathname_join(menu->detect_content_path, menu_path, content_path, @@ -3710,35 +3779,36 @@ static int action_ok_load_archive(const char *path, CORE_TYPE_PLAIN); } -static int action_ok_load_archive_detect_core(const char *path, +static int action_ok_load_archive_detect_core(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { menu_content_ctx_defer_info_t def_info; - int ret = 0; - core_info_list_t *list = NULL; - menu_handle_t *menu = NULL; - const char *menu_path = NULL; - const char *content_path = NULL; - char *new_core_path = NULL; + int ret = 0; + core_info_list_t *list = NULL; + const char *menu_path = NULL; + const char *content_path = NULL; + char *new_core_path = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); - menu_path = menu->scratch2_buf; - content_path = menu->scratch_buf; + menu_path = menu->scratch2_buf; + content_path = menu->scratch_buf; core_info_get_list(&list); - def_info.data = list; - def_info.dir = menu_path; - def_info.path = content_path; - def_info.menu_label = label; - def_info.s = menu->deferred_path; - def_info.len = sizeof(menu->deferred_path); + def_info.data = list; + def_info.dir = menu_path; + def_info.path = content_path; + def_info.menu_label = label; + def_info.s = menu->deferred_path; + def_info.len = sizeof(menu->deferred_path); - new_core_path = (char*) + new_core_path = (char*) malloc(PATH_MAX_LENGTH * sizeof(char)); - new_core_path[0] = '\0'; + new_core_path[0] = '\0'; if (menu_content_find_first_core(&def_info, false, new_core_path, PATH_MAX_LENGTH * sizeof(char))) @@ -3770,7 +3840,7 @@ static int action_ok_load_archive_detect_core(const char *path, break; case 0: idx = menu_navigation_get_selection(); - ret = generic_action_ok_displaylist_push(path, NULL, + ret = generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_DEFERRED_CORE_LIST); break; @@ -3783,9 +3853,9 @@ static int action_ok_load_archive_detect_core(const char *path, } #define default_action_ok_help(funcname, _id, _id2) \ -static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return generic_action_ok_help(path, label, type, idx, entry_idx, _id, _id2); \ + return generic_action_ok_help(data, path, label, type, idx, entry_idx, _id, _id2); \ } default_action_ok_help(action_ok_help_audio_video_troubleshooting, MENU_ENUM_LABEL_HELP_AUDIO_VIDEO_TROUBLESHOOTING, MENU_DIALOG_HELP_AUDIO_VIDEO_TROUBLESHOOTING) @@ -3796,7 +3866,8 @@ default_action_ok_help(action_ok_help_scanning_content, MENU_ENUM_LABEL_HELP_SCA default_action_ok_help(action_ok_help_change_virtual_gamepad, MENU_ENUM_LABEL_HELP_CHANGE_VIRTUAL_GAMEPAD, MENU_DIALOG_HELP_CHANGE_VIRTUAL_GAMEPAD) default_action_ok_help(action_ok_help_load_content, MENU_ENUM_LABEL_HELP_LOADING_CONTENT, MENU_DIALOG_HELP_LOADING_CONTENT) -static int action_ok_video_resolution(const char *path, +static int action_ok_video_resolution(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { unsigned width = 0; @@ -3826,7 +3897,8 @@ static int action_ok_video_resolution(const char *path, return 0; } -static int action_ok_netplay_enable_host(const char *path, +static int action_ok_netplay_enable_host(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -3898,7 +3970,8 @@ static void action_ok_netplay_enable_client_hostname_cb( } #endif -static int action_ok_netplay_enable_client(const char *path, +static int action_ok_netplay_enable_client(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -3919,7 +3992,8 @@ static int action_ok_netplay_enable_client(const char *path, return -1; } -static int action_ok_netplay_disconnect(const char *path, +static int action_ok_netplay_disconnect(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -3942,7 +4016,8 @@ static int action_ok_netplay_disconnect(const char *path, #endif } -static int action_ok_core_delete(const char *path, +static int action_ok_core_delete(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *path_core = path_get(RARCH_PATH_CORE); diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 7a6bc5e047..15c8205bf7 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -107,7 +107,11 @@ static int action_select_default(const char *path, const char *label, unsigned t static int action_select_path_use_directory(const char *path, const char *label, unsigned type, size_t idx) { - return action_ok_path_use_directory(path, label, type, idx, 0 /* unused */); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return action_ok_path_use_directory(menu, + path, label, type, idx, 0 /* unused */); } static int action_select_driver_setting(const char *path, const char *label, unsigned type, diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 3be1f16e85..b5f14aa2c3 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -133,21 +133,26 @@ int action_refresh_default(file_list_t *list, file_list_t *menu_list); int shader_action_parameter_right(unsigned type, const char *label, bool wraparound); -int generic_action_ok_displaylist_push(const char *path, const char *new_path, +int generic_action_ok_displaylist_push(void *data, + const char *path, const char *new_path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned action_type); +int action_ok_push_generic_list(void *data, + const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx); + +int action_ok_path_use_directory(void *data, + const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx); + +int action_ok_directory_push(void *data, + const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx); + int generic_action_cheat_toggle(size_t idx, unsigned type, const char *label, bool wraparound); -int action_ok_push_generic_list(const char *path, - const char *label, unsigned type, size_t idx, size_t entry_idx); - -int action_ok_path_use_directory(const char *path, - const char *label, unsigned type, size_t idx, size_t entry_idx); - -int action_ok_directory_push(const char *path, - const char *label, unsigned type, size_t idx, size_t entry_idx); int core_setting_right(unsigned type, const char *label, bool wraparound); diff --git a/menu/menu_driver.c b/menu/menu_driver.c index ddbadabe02..82b2932143 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -1706,7 +1706,8 @@ bool menu_driver_iterate(menu_ctx_iterate_t *iterate) menu_entries_flush_stack(NULL, MENU_SETTINGS); menu_display_set_msg_force(true); - generic_action_ok_displaylist_push("", NULL, + generic_action_ok_displaylist_push(menu_driver_data, + "", NULL, "", 0, 0, 0, ACTION_OK_DL_CONTENT_SETTINGS); if (menu_driver_pending_quit) diff --git a/menu/menu_entries.h b/menu/menu_entries.h index c669f9b2af..3712dc4d1b 100644 --- a/menu/menu_entries.h +++ b/menu/menu_entries.h @@ -107,7 +107,8 @@ typedef struct menu_file_list_cbs size_t idx); int (*action_get_title)(const char *path, const char *label, unsigned type, char *s, size_t len); - int (*action_ok)(const char *path, const char *label, unsigned type, + int (*action_ok)(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx); int (*action_cancel)(const char *path, const char *label, unsigned type, size_t idx); diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index ea5be0c4cc..c0c28b8fdd 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -448,9 +448,15 @@ int menu_entry_action(menu_entry_t *entry, unsigned i, enum menu_action action) break; case MENU_ACTION_OK: - if (cbs && cbs->action_ok) - ret = cbs->action_ok(entry->path, - entry->label, entry->type, i, entry->entry_idx); + { + menu_handle_t *menu = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (cbs && cbs->action_ok) + ret = cbs->action_ok(menu, entry->path, + entry->label, entry->type, i, entry->entry_idx); + } break; case MENU_ACTION_START: if (cbs && cbs->action_start) From 7c314a91650c9b4d74222bbdefa37e014fa4ae0d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 19:17:51 +0200 Subject: [PATCH 246/517] Start passing around menu_driver_data around properly --- menu/cbs/menu_cbs_select.c | 8 +++++++- menu/drivers/materialui.c | 27 ++++++++++++--------------- menu/drivers/menu_generic.c | 2 +- menu/drivers/rgui.c | 7 +++++-- menu/drivers/xmb.c | 7 ++++--- menu/menu_driver.c | 11 ++++++++--- menu/menu_driver.h | 9 ++++++--- menu/menu_input.c | 30 ++++++++++++++++++++++++------ menu/widgets/menu_entry.c | 26 ++++++++++++++------------ menu/widgets/menu_entry.h | 3 ++- 10 files changed, 83 insertions(+), 47 deletions(-) diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 15c8205bf7..8d31efe823 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -95,7 +95,13 @@ static int action_select_default(const char *path, const char *label, unsigned t } if (action != MENU_ACTION_NOOP) - ret = menu_entry_action(&entry, (unsigned)idx, action); + { + menu_handle_t *menu = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + ret = menu_entry_action(&entry, menu, (unsigned)idx, action); + } menu_entry_free(&entry); diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 8d4e0deb7b..f21a50afc2 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -2268,7 +2268,8 @@ static size_t materialui_list_get_selection(void *data) /* The pointer or the mouse is pressed down. We use this callback to highlight the entry that has been pressed */ -static int materialui_pointer_down(void *userdata, +static int materialui_pointer_down(void *data, + void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) @@ -2276,22 +2277,16 @@ static int materialui_pointer_down(void *userdata, unsigned width, height; unsigned header_height; size_t entries_end = menu_entries_get_size(); - materialui_handle_t *mui = (materialui_handle_t*)userdata; + materialui_handle_t *mui = (materialui_handle_t*)userdata; if (!mui) return 0; - header_height = menu_display_get_header_height(); + header_height = menu_display_get_header_height(); video_driver_get_size(&width, &height); - if (y < header_height) - { - - } - else if (y > height - mui->tabs_height) - { - - } + if (y < header_height) { } + else if (y > height - mui->tabs_height) { } else if (ptr <= (entries_end - 1)) { size_t ii; @@ -2318,7 +2313,8 @@ static int materialui_pointer_down(void *userdata, If we clicked on the header, we perform a cancel action. If we clicked on the tabs, we switch to a new list. If we clicked on a menu entry, we call the entry action callback. */ -static int materialui_pointer_up(void *userdata, +static int materialui_pointer_up(void *data, + void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) @@ -2326,7 +2322,7 @@ static int materialui_pointer_up(void *userdata, unsigned width, height; unsigned header_height, i; size_t entries_end = menu_entries_get_size(); - materialui_handle_t *mui = (materialui_handle_t*)userdata; + materialui_handle_t *mui = (materialui_handle_t*)userdata; if (!mui) return 0; @@ -2337,7 +2333,7 @@ static int materialui_pointer_up(void *userdata, if (y < header_height) { size_t selection = menu_navigation_get_selection(); - return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL); + return menu_entry_action(entry, data, (unsigned)selection, MENU_ACTION_CANCEL); } else if (y > height - mui->tabs_height) { @@ -2376,7 +2372,8 @@ static int materialui_pointer_up(void *userdata, ) { if (ptr == ii && cbs && cbs->action_select) - return menu_entry_action(entry, (unsigned)ii, MENU_ACTION_SELECT); + return menu_entry_action(entry, data, + (unsigned)ii, MENU_ACTION_SELECT); } } } diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 3661d5d5e2..4566b79c67 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -229,7 +229,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) menu_entry_init(&entry); menu_entry_get(&entry, 0, selection, NULL, false); - ret = menu_entry_action(&entry, + ret = menu_entry_action(&entry, menu, (unsigned)selection, (enum menu_action)action); menu_entry_free(&entry); if (ret) diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 16de9c2c43..0c746d7cbd 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -868,6 +868,7 @@ static int rgui_environ(enum menu_environ_cb type, } static int rgui_pointer_tap(void *data, + void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) @@ -877,14 +878,16 @@ static int rgui_pointer_tap(void *data, if (y < header_height) { size_t selection = menu_navigation_get_selection(); - return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL); + return menu_entry_action(entry, data, + (unsigned)selection, MENU_ACTION_CANCEL); } else if (ptr <= (menu_entries_get_size() - 1)) { size_t selection = menu_navigation_get_selection(); if (ptr == selection && cbs && cbs->action_select) - return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT); + return menu_entry_action(entry, data, + (unsigned)selection, MENU_ACTION_SELECT); menu_navigation_set_selection(ptr); menu_driver_navigation_set(false); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 53ca07b3d7..ce2c0ee83b 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -5132,7 +5132,8 @@ error: return false; } -static int xmb_pointer_tap(void *userdata, +static int xmb_pointer_tap(void *data, + void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) @@ -5142,13 +5143,13 @@ static int xmb_pointer_tap(void *userdata, if (y < header_height) { size_t selection = menu_navigation_get_selection(); - return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL); + return (unsigned)menu_entry_action(entry, data, (unsigned)selection, MENU_ACTION_CANCEL); } else if (ptr <= (menu_entries_get_size() - 1)) { size_t selection = menu_navigation_get_selection(); if (ptr == selection && cbs && cbs->action_select) - return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT); + return (unsigned)menu_entry_action(entry, data, (unsigned)selection, MENU_ACTION_SELECT); menu_navigation_set_selection(ptr); menu_driver_navigation_set(false); diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 82b2932143..9e8015c55b 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -2119,7 +2119,8 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) point->retcode = 0; return false; } - point->retcode = menu_driver_ctx->pointer_tap(menu_userdata, + point->retcode = menu_driver_ctx->pointer_tap(menu_driver_data, + menu_userdata, point->x, point->y, point->ptr, point->cbs, point->entry, point->action); } @@ -2132,7 +2133,9 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) point->retcode = 0; return false; } - point->retcode = menu_driver_ctx->pointer_down(menu_userdata, + point->retcode = menu_driver_ctx->pointer_down( + menu_driver_data, + menu_userdata, point->x, point->y, point->ptr, point->cbs, point->entry, point->action); } @@ -2145,7 +2148,9 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) point->retcode = 0; return false; } - point->retcode = menu_driver_ctx->pointer_up(menu_userdata, + point->retcode = menu_driver_ctx->pointer_up( + menu_driver_data, + menu_userdata, point->x, point->y, point->ptr, point->cbs, point->entry, point->action); } diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 412e920478..c9cbc48de9 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -486,7 +486,8 @@ typedef struct menu_ctx_driver bool (*load_image)(void *userdata, void *data, enum menu_image_type type); const char *ident; int (*environ_cb)(enum menu_environ_cb type, void *data, void *userdata); - int (*pointer_tap)(void *data, unsigned x, unsigned y, unsigned ptr, + int (*pointer_tap)(void *data, void *userdata, + unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action); void (*update_thumbnail_path)(void *data, unsigned i, char pos); @@ -496,10 +497,12 @@ typedef struct menu_ctx_driver int (*osk_ptr_at_pos)(void *data, int x, int y, unsigned width, unsigned height); void (*update_savestate_thumbnail_path)(void *data, unsigned i); void (*update_savestate_thumbnail_image)(void *data); - int (*pointer_down)(void *data, unsigned x, unsigned y, unsigned ptr, + int (*pointer_down)(void *data, void *userdata, + unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action); - int (*pointer_up)(void *data, unsigned x, unsigned y, unsigned ptr, + int (*pointer_up)(void *data, void *userdata, + unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action); } menu_ctx_driver_t; diff --git a/menu/menu_input.c b/menu/menu_input.c index 8fdeb4f106..a791ad83d3 100644 --- a/menu/menu_input.c +++ b/menu/menu_input.c @@ -286,8 +286,14 @@ static int menu_input_mouse_frame( if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_BUTTON_R)) { - size_t selection = menu_navigation_get_selection(); - menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL); + size_t selection = menu_navigation_get_selection(); + menu_handle_t *menu = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + menu_entry_action(entry, menu, + (unsigned)selection, + MENU_ACTION_CANCEL); } if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_WHEEL_DOWN)) @@ -521,9 +527,15 @@ static int menu_input_pointer_post_iterate( { if (menu_input->pointer.counter > 32) { - size_t selection = menu_navigation_get_selection(); + size_t selection = menu_navigation_get_selection(); + menu_handle_t *menu = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + if (cbs && cbs->action_start) - return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_START); + return menu_entry_action(entry, menu, + (unsigned)selection, + MENU_ACTION_START); } else @@ -552,8 +564,14 @@ static int menu_input_pointer_post_iterate( { if (!pointer_oldback) { - pointer_oldback = true; - menu_entry_action(entry, (unsigned)menu_navigation_get_selection(), MENU_ACTION_CANCEL); + menu_handle_t *menu = NULL; + pointer_oldback = true; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + menu_entry_action(entry, menu, + (unsigned)menu_navigation_get_selection(), + MENU_ACTION_CANCEL); } } diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index c0c28b8fdd..6e6aea9007 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -254,12 +254,15 @@ void menu_entry_pathdir_extensions(uint32_t i, char *s, size_t len) void menu_entry_reset(uint32_t i) { + menu_handle_t *menu = NULL; menu_entry_t entry; menu_entry_init(&entry); menu_entry_get(&entry, 0, i, NULL, true); - menu_entry_action(&entry, i, MENU_ACTION_START); + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + menu_entry_action(&entry, menu, i, MENU_ACTION_START); } void menu_entry_get_value(menu_entry_t *entry, char *s, size_t len) @@ -408,16 +411,21 @@ bool menu_entry_is_currently_selected(unsigned id) int menu_entry_select(uint32_t i) { menu_entry_t entry; + menu_handle_t *menu = NULL; menu_navigation_set_selection(i); menu_entry_init(&entry); menu_entry_get(&entry, 0, i, NULL, false); - return menu_entry_action(&entry, i, MENU_ACTION_SELECT); + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + return menu_entry_action(&entry, menu, i, MENU_ACTION_SELECT); } -int menu_entry_action(menu_entry_t *entry, unsigned i, enum menu_action action) +int menu_entry_action(menu_entry_t *entry, + void *data, + unsigned i, enum menu_action action) { int ret = 0; file_list_t *selection_buf = @@ -448,15 +456,9 @@ int menu_entry_action(menu_entry_t *entry, unsigned i, enum menu_action action) break; case MENU_ACTION_OK: - { - menu_handle_t *menu = NULL; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (cbs && cbs->action_ok) - ret = cbs->action_ok(menu, entry->path, - entry->label, entry->type, i, entry->entry_idx); - } + if (cbs && cbs->action_ok) + ret = cbs->action_ok(data, entry->path, + entry->label, entry->type, i, entry->entry_idx); break; case MENU_ACTION_START: if (cbs && cbs->action_start) diff --git a/menu/widgets/menu_entry.h b/menu/widgets/menu_entry.h index b32ec148ee..94ffb13f8b 100644 --- a/menu/widgets/menu_entry.h +++ b/menu/widgets/menu_entry.h @@ -113,7 +113,8 @@ void menu_entry_get(menu_entry_t *entry, size_t stack_idx, int menu_entry_select(uint32_t i); int menu_entry_action(menu_entry_t *entry, - unsigned i, enum menu_action action); + void *data, + unsigned i, enum menu_action action); void menu_entry_free(menu_entry_t *entry); From 21ede63df2ad2bd7d6d0f92b107d9202746f1c23 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 19:20:50 +0200 Subject: [PATCH 247/517] Update --- menu/cbs/menu_cbs_info.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/menu/cbs/menu_cbs_info.c b/menu/cbs/menu_cbs_info.c index 9694ddf26c..453d5aeec6 100644 --- a/menu/cbs/menu_cbs_info.c +++ b/menu/cbs/menu_cbs_info.c @@ -60,17 +60,21 @@ error: } #ifdef HAVE_CHEEVOS -int generic_action_ok_help(const char *path, +int generic_action_ok_help(void *data, + const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums id, enum menu_dialog_type id2); static int action_info_cheevos(unsigned type, const char *label) { + menu_handle_t *menu = NULL; unsigned new_id = type - MENU_SETTINGS_CHEEVOS_START; menu_dialog_set_current_id(new_id); - return generic_action_ok_help(NULL, label, new_id, 0, 0, + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + return generic_action_ok_help(menu, NULL, label, new_id, 0, 0, MENU_ENUM_LABEL_CHEEVOS_DESCRIPTION, MENU_DIALOG_HELP_CHEEVOS_DESCRIPTION); } From 15142fd0a98b0761c883366f817dd92326352ab9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 19:22:39 +0200 Subject: [PATCH 248/517] (iOS) Buildfix --- ui/drivers/cocoa/cocoatouch_menu.m | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/drivers/cocoa/cocoatouch_menu.m b/ui/drivers/cocoa/cocoatouch_menu.m index 500591aee8..4ec02d1e1f 100644 --- a/ui/drivers/cocoa/cocoatouch_menu.m +++ b/ui/drivers/cocoa/cocoatouch_menu.m @@ -817,11 +817,14 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath - (void)menuBack { #ifdef HAVE_MENU - menu_entry_t entry = {0}; - size_t selection = menu_navigation_get_selection(); + menu_handle_t *menu = NULL; + menu_entry_t entry = {0}; + size_t selection = menu_navigation_get_selection(); + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); menu_entry_get(&entry, 0, selection, NULL, false); - menu_entry_action(&entry, (unsigned int)selection, MENU_ACTION_CANCEL); + menu_entry_action(&entry, menu, (unsigned int)selection, MENU_ACTION_CANCEL); #endif } From b4a2bd5156354b084283165be96d43ee99bf50ec Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 19:32:50 +0200 Subject: [PATCH 249/517] Pass around void pointer data - menu handle --- menu/cbs/menu_cbs_deferred_push.c | 51 ++++++++++++++++++------------- menu/drivers/xmb.c | 3 +- menu/menu_displaylist.c | 7 +++-- menu/menu_entries.h | 2 +- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index 648caf0128..d93cd79a62 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -38,7 +38,7 @@ #ifndef BIND_ACTION_DEFERRED_PUSH #define BIND_ACTION_DEFERRED_PUSH(cbs, name) \ - cbs->action_deferred_push = name; \ + cbs->action_deferred_push = name; \ cbs->action_deferred_push_ident = #name; #endif @@ -50,7 +50,8 @@ enum PUSH_DETECT_CORE_LIST }; -static int deferred_push_dlist(menu_displaylist_info_t *info, enum menu_displaylist_ctl_state state) +static int deferred_push_dlist(menu_displaylist_info_t *info, + void *data, enum menu_displaylist_ctl_state state) { if (!menu_displaylist_ctl(state, info)) return menu_cbs_exit(); @@ -59,7 +60,7 @@ static int deferred_push_dlist(menu_displaylist_info_t *info, enum menu_displayl } static int deferred_push_database_manager_list_deferred( - menu_displaylist_info_t *info) + menu_displaylist_info_t *info, void *data) { if (!string_is_empty(info->path_b)) free(info->path_b); @@ -69,13 +70,13 @@ static int deferred_push_database_manager_list_deferred( info->path_b = strdup(info->path); info->path_c = NULL; - return deferred_push_dlist(info, DISPLAYLIST_DATABASE_QUERY); + return deferred_push_dlist(info, data, DISPLAYLIST_DATABASE_QUERY); } #define generic_deferred_push(name, type) \ -static int (name)(menu_displaylist_info_t *info) \ +static int (name)(menu_displaylist_info_t *info, void *data) \ { \ - return deferred_push_dlist(info, type); \ + return deferred_push_dlist(info, data, type); \ } generic_deferred_push(deferred_push_video_shader_preset_parameters, DISPLAYLIST_SHADER_PARAMETERS_PRESET) @@ -175,7 +176,7 @@ generic_deferred_push(deferred_push_lakka_list, DISPLAYLIST_ #endif static int deferred_push_cursor_manager_list_deferred( - menu_displaylist_info_t *info) + menu_displaylist_info_t *info, void *data) { char rdb_path[PATH_MAX_LENGTH]; int ret = -1; @@ -214,7 +215,8 @@ static int deferred_push_cursor_manager_list_deferred( info->path_c = strdup(query); info->path = strdup(rdb_path); - ret = deferred_push_dlist(info, DISPLAYLIST_DATABASE_QUERY); + ret = deferred_push_dlist(info, data, + DISPLAYLIST_DATABASE_QUERY); end: if (conf) @@ -227,7 +229,8 @@ end: #ifdef HAVE_LIBRETRODB static int deferred_push_cursor_manager_list_generic( - menu_displaylist_info_t *info, enum database_query_type type) + menu_displaylist_info_t *info, void *data, + enum database_query_type type) { char query[PATH_MAX_LENGTH]; int ret = -1; @@ -239,7 +242,8 @@ static int deferred_push_cursor_manager_list_generic( query[0] = '\0'; - database_info_build_query_enum(query, sizeof(query), type, str_list->elems[0].data); + database_info_build_query_enum(query, + sizeof(query), type, str_list->elems[0].data); if (string_is_empty(query)) goto end; @@ -255,7 +259,8 @@ static int deferred_push_cursor_manager_list_generic( info->path_b = strdup(str_list->elems[0].data); info->path_c = strdup(query); - ret = deferred_push_dlist(info, DISPLAYLIST_DATABASE_QUERY); + ret = deferred_push_dlist(info, data, + DISPLAYLIST_DATABASE_QUERY); end: string_list_free(str_list); @@ -263,9 +268,9 @@ end: } #define generic_deferred_cursor_manager(name, type) \ -static int (name)(menu_displaylist_info_t *info) \ +static int (name)(menu_displaylist_info_t *info, void *data) \ { \ - return deferred_push_cursor_manager_list_generic(info, type); \ + return deferred_push_cursor_manager_list_generic(info, data, type); \ } generic_deferred_cursor_manager(deferred_push_cursor_manager_list_deferred_query_rdb_entry_max_users, DATABASE_QUERY_ENTRY_MAX_USERS) @@ -289,7 +294,7 @@ generic_deferred_cursor_manager(deferred_push_cursor_manager_list_deferred_query #if 0 static int deferred_push_cursor_manager_list_deferred_query_subsearch( - menu_displaylist_info_t *info) + menu_displaylist_info_t *info, void *data) { int ret = -1; #ifdef HAVE_LIBRETRODB @@ -314,7 +319,8 @@ static int deferred_push_cursor_manager_list_deferred_query_subsearch( info->path_b = strdup(str_list->elems[0].data); info->path_c = strdup(query); - ret = deferred_push_dlist(info, DISPLAYLIST_DATABASE_QUERY); + ret = deferred_push_dlist(info, data, + DISPLAYLIST_DATABASE_QUERY); end: string_list_free(str_list); @@ -324,16 +330,17 @@ end: #endif static int general_push(menu_displaylist_info_t *info, + void *data, unsigned id, enum menu_displaylist_ctl_state state) { settings_t *settings = config_get_ptr(); char *newstring2 = NULL; core_info_list_t *list = NULL; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; rarch_system_info_t *system = runloop_get_system_info(); struct retro_system_info *system_menu = &system->info; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!data) return menu_cbs_exit(); core_info_get_list(&list); @@ -562,20 +569,20 @@ static int general_push(menu_displaylist_info_t *info, } free(newstring2); - return deferred_push_dlist(info, state); + return deferred_push_dlist(info, data, state); } #define generic_deferred_push_general(name, a, b) \ -static int (name)(menu_displaylist_info_t *info) \ +static int (name)(menu_displaylist_info_t *info, void *data) \ { \ - return general_push(info, a, b); \ + return general_push(info, data, a, b); \ } #define generic_deferred_push_clear_general(name, a, b) \ -static int (name)(menu_displaylist_info_t *info) \ +static int (name)(menu_displaylist_info_t *info, void *data) \ { \ menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); \ - return general_push(info, a, b); \ + return general_push(info, data, a, b); \ } generic_deferred_push_general(deferred_push_detect_core_list, PUSH_DETECT_CORE_LIST, DISPLAYLIST_CORES_DETECTED) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index ce2c0ee83b..0391a439bb 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -4829,7 +4829,8 @@ static void xmb_toggle(void *userdata, bool menu_on) xmb_toggle_horizontal_list(xmb); } -static int deferred_push_content_actions(menu_displaylist_info_t *info) +static int deferred_push_content_actions(menu_displaylist_info_t *info, + void *data) { if (!menu_displaylist_ctl( DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info)) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index e370a774de..6b6c68251c 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3930,9 +3930,12 @@ bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry) unsigned type = 0; bool ret = false; enum msg_hash_enums enum_idx = MSG_UNKNOWN; + menu_handle_t *menu = NULL; if (!entry) return false; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return false; menu_displaylist_info_init(&info); @@ -3961,10 +3964,8 @@ bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry) cbs = menu_entries_get_last_stack_actiondata(); if (cbs && cbs->action_deferred_push) - { - if (cbs->action_deferred_push(&info) != 0) + if (cbs->action_deferred_push(&info, menu) != 0) goto error; - } ret = true; diff --git a/menu/menu_entries.h b/menu/menu_entries.h index 3712dc4d1b..57e5f1b604 100644 --- a/menu/menu_entries.h +++ b/menu/menu_entries.h @@ -102,7 +102,7 @@ typedef struct menu_file_list_cbs rarch_setting_t *setting; int (*action_iterate)(const char *label, unsigned action); - int (*action_deferred_push)(menu_displaylist_info_t *info); + int (*action_deferred_push)(menu_displaylist_info_t *info, void *data); int (*action_select)(const char *path, const char *label, unsigned type, size_t idx); int (*action_get_title)(const char *path, const char *label, From fbf03df8be22b40a1d44908ebfe0163a0ebb4bef Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 19:57:33 +0200 Subject: [PATCH 250/517] Less pointer grabbbing - reduce usage of RARCH_MENU_CTL_DRIVER_DATA_GET --- menu/cbs/menu_cbs_deferred_push.c | 2 +- menu/cbs/menu_cbs_info.c | 5 +- menu/cbs/menu_cbs_ok.c | 2 +- menu/drivers/materialui.c | 36 +++++------ menu/drivers/menu_generic.c | 2 +- menu/drivers/xmb.c | 57 +++++++++-------- menu/menu_displaylist.c | 88 +++++++++++---------------- menu/menu_displaylist.h | 3 +- menu/menu_driver.c | 3 +- menu/menu_driver.h | 2 +- menu/menu_setting.c | 10 ++- menu/widgets/menu_dialog.c | 7 ++- menu/widgets/menu_input_bind_dialog.c | 10 ++- 13 files changed, 118 insertions(+), 109 deletions(-) diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index d93cd79a62..df640bda49 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -53,7 +53,7 @@ enum static int deferred_push_dlist(menu_displaylist_info_t *info, void *data, enum menu_displaylist_ctl_state state) { - if (!menu_displaylist_ctl(state, info)) + if (!menu_displaylist_ctl(state, info, data)) return menu_cbs_exit(); menu_displaylist_process(info); return 0; diff --git a/menu/cbs/menu_cbs_info.c b/menu/cbs/menu_cbs_info.c index 453d5aeec6..f6d673a212 100644 --- a/menu/cbs/menu_cbs_info.c +++ b/menu/cbs/menu_cbs_info.c @@ -33,6 +33,7 @@ static int action_info_default(unsigned type, const char *label) { menu_displaylist_info_t info; + menu_handle_t *menu = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); size_t selection = menu_navigation_get_selection(); @@ -44,7 +45,9 @@ static int action_info_default(unsigned type, const char *label) info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_INFO_SCREEN)); - if (!menu_displaylist_ctl(DISPLAYLIST_HELP, &info)) + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (!menu_displaylist_ctl(DISPLAYLIST_HELP, &info, menu)) goto error; if (!menu_displaylist_process(&info)) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index c053fbb285..a689c3ced4 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -834,7 +834,7 @@ int generic_action_ok_displaylist_push( if (info_path) info.path = strdup(info_path); - if (menu_displaylist_ctl(dl_type, &info)) + if (menu_displaylist_ctl(dl_type, &info, menu)) { if (menu_displaylist_process(&info)) { diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index f21a50afc2..1e0c9344eb 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -1962,9 +1962,11 @@ static void materialui_populate_entries( } /* Context reset is called on launch or when a core is launched */ -static void materialui_context_reset(void *data, bool is_threaded) +static void materialui_context_reset(void *data, void *userdata, + bool is_threaded) { - materialui_handle_t *mui = (materialui_handle_t*)data; + menu_handle_t *menu = (menu_handle_t*)data; + materialui_handle_t *mui = (materialui_handle_t*)userdata; settings_t *settings = config_get_ptr(); if (!mui || !settings) @@ -2160,7 +2162,7 @@ static int materialui_list_push(void *data, void *userdata, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE))) { entry.enum_idx = MENU_ENUM_LABEL_CONTENT_SETTINGS; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } #ifndef HAVE_DYNAMIC @@ -2170,39 +2172,39 @@ static int materialui_list_push(void *data, void *userdata, if (settings->bools.menu_show_load_core) { entry.enum_idx = MENU_ENUM_LABEL_CORE_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } } if (system->load_no_content) { entry.enum_idx = MENU_ENUM_LABEL_START_CORE; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } if (settings->bools.menu_show_load_content) { entry.enum_idx = MENU_ENUM_LABEL_LOAD_CONTENT_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } if (settings->bools.menu_content_show_history) { entry.enum_idx = MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } #if defined(HAVE_NETWORKING) #ifdef HAVE_LAKKA entry.enum_idx = MENU_ENUM_LABEL_UPDATE_LAKKA; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); #else { settings_t *settings = config_get_ptr(); if (settings->bools.menu_show_online_updater) { entry.enum_idx = MENU_ENUM_LABEL_ONLINE_UPDATER; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } } #endif @@ -2210,42 +2212,42 @@ static int materialui_list_push(void *data, void *userdata, if (settings->bools.menu_content_show_netplay) { entry.enum_idx = MENU_ENUM_LABEL_NETPLAY; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } #endif if (settings->bools.menu_show_information) { entry.enum_idx = MENU_ENUM_LABEL_INFORMATION_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } #ifndef HAVE_DYNAMIC entry.enum_idx = MENU_ENUM_LABEL_RESTART_RETROARCH; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); #endif if (settings->bools.menu_show_configurations) { entry.enum_idx = MENU_ENUM_LABEL_CONFIGURATIONS_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } if (settings->bools.menu_show_help) { entry.enum_idx = MENU_ENUM_LABEL_HELP_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } #if !defined(IOS) entry.enum_idx = MENU_ENUM_LABEL_QUIT_RETROARCH; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); #endif #if defined(HAVE_LAKKA) if (settings->bools.menu_show_reboot) { entry.enum_idx = MENU_ENUM_LABEL_REBOOT; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } entry.enum_idx = MENU_ENUM_LABEL_SHUTDOWN; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); #endif info->need_push = true; ret = 0; diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 4566b79c67..2424c888bd 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -282,7 +282,7 @@ bool generic_menu_init_list(void *data) info.list = selection_buf; - if (menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info)) + if (menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info, data)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 0391a439bb..d9d77bb0d2 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -1973,7 +1973,7 @@ static void xmb_context_destroy_horizontal_list(xmb_handle_t *xmb) } } -static void xmb_init_horizontal_list(xmb_handle_t *xmb) +static void xmb_init_horizontal_list(menu_handle_t *menu, xmb_handle_t *xmb) { menu_displaylist_info_t info; settings_t *settings = config_get_ptr(); @@ -1992,7 +1992,8 @@ static void xmb_init_horizontal_list(xmb_handle_t *xmb) if (!string_is_empty(info.path)) { - if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL, &info)) + if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL, + &info, menu)) { size_t i; for (i = 0; i < xmb->horizontal_list->size; i++) @@ -2031,6 +2032,7 @@ static void xmb_toggle_horizontal_list(xmb_handle_t *xmb) } static void xmb_context_reset_horizontal_list( + menu_handle_t *menu, xmb_handle_t *xmb) { unsigned i; @@ -2134,7 +2136,7 @@ static void xmb_context_reset_horizontal_list( xmb_toggle_horizontal_list(xmb); } -static void xmb_refresh_horizontal_list(xmb_handle_t *xmb) +static void xmb_refresh_horizontal_list(menu_handle_t *menu, xmb_handle_t *xmb) { xmb_context_destroy_horizontal_list(xmb); if (xmb->horizontal_list) @@ -2150,9 +2152,9 @@ static void xmb_refresh_horizontal_list(xmb_handle_t *xmb) calloc(1, sizeof(file_list_t)); if (xmb->horizontal_list) - xmb_init_horizontal_list(xmb); + xmb_init_horizontal_list(menu, xmb); - xmb_context_reset_horizontal_list(xmb); + xmb_context_reset_horizontal_list(menu, xmb); } static int xmb_environ(enum menu_environ_cb type, void *data, void *userdata) @@ -2175,7 +2177,7 @@ static int xmb_environ(enum menu_environ_cb type, void *data, void *userdata) if (!xmb) return -1; - xmb_refresh_horizontal_list(xmb); + xmb_refresh_horizontal_list((menu_handle_t*)data, xmb); break; default: return -1; @@ -4099,7 +4101,7 @@ static void *xmb_init(void **userdata, bool video_is_threaded) xmb->horizontal_list = (file_list_t*)calloc(1, sizeof(file_list_t)); if (xmb->horizontal_list) - xmb_init_horizontal_list(xmb); + xmb_init_horizontal_list(menu, xmb); xmb_init_ribbon(xmb); @@ -4433,9 +4435,10 @@ static void xmb_context_reset_background(const char *iconpath) free(path); } -static void xmb_context_reset(void *data, bool is_threaded) +static void xmb_context_reset(void *data, void *userdata, bool is_threaded) { - xmb_handle_t *xmb = (xmb_handle_t*)data; + menu_handle_t *menu = (menu_handle_t*)data; + xmb_handle_t *xmb = (xmb_handle_t*)userdata; if (xmb) { @@ -4466,7 +4469,7 @@ static void xmb_context_reset(void *data, bool is_threaded) is_threaded); xmb_context_reset_textures(xmb, iconpath); xmb_context_reset_background(iconpath); - xmb_context_reset_horizontal_list(xmb); + xmb_context_reset_horizontal_list(menu, xmb); if (!string_is_equal(xmb_thumbnails_ident('R'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) @@ -4833,7 +4836,7 @@ static int deferred_push_content_actions(menu_displaylist_info_t *info, void *data) { if (!menu_displaylist_ctl( - DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info)) + DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info, data)) return -1; menu_displaylist_process(info); menu_displaylist_info_free(info); @@ -4943,13 +4946,13 @@ static int xmb_list_push(void *data, void *userdata, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE))) { entry.enum_idx = MENU_ENUM_LABEL_CONTENT_SETTINGS; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } if (system->load_no_content) { entry.enum_idx = MENU_ENUM_LABEL_START_CORE; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } #ifndef HAVE_DYNAMIC @@ -4959,7 +4962,7 @@ static int xmb_list_push(void *data, void *userdata, if (settings->bools.menu_show_load_core) { entry.enum_idx = MENU_ENUM_LABEL_CORE_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } } @@ -4968,7 +4971,7 @@ static int xmb_list_push(void *data, void *userdata, const struct retro_subsystem_info* subsystem = NULL; entry.enum_idx = MENU_ENUM_LABEL_LOAD_CONTENT_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); subsystem = system->subsystem.data; @@ -5024,68 +5027,68 @@ static int xmb_list_push(void *data, void *userdata, } entry.enum_idx = MENU_ENUM_LABEL_ADD_CONTENT_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); #if defined(HAVE_NETWORKING) { settings_t *settings = config_get_ptr(); if (settings->bools.menu_show_online_updater && !settings->bools.kiosk_mode_enable) { entry.enum_idx = MENU_ENUM_LABEL_ONLINE_UPDATER; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } } #endif if (!settings->bools.menu_content_show_settings && !string_is_empty(settings->paths.menu_content_show_settings_password)) { entry.enum_idx = MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } if (settings->bools.kiosk_mode_enable && !string_is_empty(settings->paths.kiosk_mode_password)) { entry.enum_idx = MENU_ENUM_LABEL_MENU_DISABLE_KIOSK_MODE; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } if (settings->bools.menu_show_information) { entry.enum_idx = MENU_ENUM_LABEL_INFORMATION_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } #ifndef HAVE_DYNAMIC entry.enum_idx = MENU_ENUM_LABEL_RESTART_RETROARCH; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); #endif if (settings->bools.menu_show_configurations && !settings->bools.kiosk_mode_enable) { entry.enum_idx = MENU_ENUM_LABEL_CONFIGURATIONS_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } if (settings->bools.menu_show_help) { entry.enum_idx = MENU_ENUM_LABEL_HELP_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } #if !defined(IOS) if (settings->bools.menu_show_quit_retroarch) { entry.enum_idx = MENU_ENUM_LABEL_QUIT_RETROARCH; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } #endif if (settings->bools.menu_show_reboot) { entry.enum_idx = MENU_ENUM_LABEL_REBOOT; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); } entry.enum_idx = MENU_ENUM_LABEL_SHUTDOWN; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); info->need_push = true; ret = 0; } @@ -5117,7 +5120,7 @@ static bool xmb_menu_init_list(void *data) info.list = selection_buf; - if (!menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info)) + if (!menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info, data)) goto error; info.need_push = true; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 6b6c68251c..e192883774 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -1585,7 +1585,8 @@ static enum msg_file_type extension_to_file_hash_type(const char *ext) return FILE_TYPE_NONE; } -static int menu_displaylist_parse_database_entry(menu_displaylist_info_t *info) +static int menu_displaylist_parse_database_entry(menu_displaylist_info_t *info, + menu_handle_t *menu) { unsigned i, j, k; char path_playlist[PATH_MAX_LENGTH]; @@ -1593,14 +1594,10 @@ static int menu_displaylist_parse_database_entry(menu_displaylist_info_t *info) char query[PATH_MAX_LENGTH]; playlist_t *playlist = NULL; database_info_list_t *db_info = NULL; - menu_handle_t *menu = NULL; settings_t *settings = config_get_ptr(); path_playlist[0] = path_base[0] = query[0] = '\0'; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - goto error; - database_info_build_query_enum(query, sizeof(query), DATABASE_QUERY_ENTRY, info->path_b); @@ -2500,13 +2497,13 @@ static void menu_displaylist_set_new_playlist( static int menu_displaylist_parse_horizontal_list( - menu_displaylist_info_t *info) + menu_displaylist_info_t *info, + menu_handle_t *menu) { menu_ctx_list_t list_info; menu_ctx_list_t list_horiz_info; bool is_historylist = false; playlist_t *playlist = NULL; - menu_handle_t *menu = NULL; struct item_file *item = NULL; settings_t *settings = config_get_ptr(); @@ -2522,9 +2519,6 @@ static int menu_displaylist_parse_horizontal_list( item = (struct item_file*)list_horiz_info.entry; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return -1; - if (!item) return -1; @@ -2562,14 +2556,10 @@ static int menu_displaylist_parse_horizontal_list( } static int menu_displaylist_parse_load_content_settings( - menu_displaylist_info_t *info) + menu_displaylist_info_t *info, menu_handle_t *menu) { - menu_handle_t *menu = NULL; settings_t *settings = config_get_ptr(); - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return -1; - if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) { #ifdef HAVE_LAKKA @@ -2770,24 +2760,18 @@ static int menu_displaylist_parse_load_content_settings( } static int menu_displaylist_parse_horizontal_content_actions( - menu_displaylist_info_t *info) + menu_displaylist_info_t *info, menu_handle_t *menu) { bool content_loaded = false; - unsigned idx = 0; - menu_handle_t *menu = NULL; const char *label = NULL; const char *entry_path = NULL; const char *core_path = NULL; const char *core_name = NULL; const char *db_name = NULL; - playlist_t *playlist = playlist_get_cached(); settings_t *settings = config_get_ptr(); const char *fullpath = path_get(RARCH_PATH_CONTENT); - - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return -1; - - idx = menu->rpl_entry_selection_ptr; + unsigned idx = menu->rpl_entry_selection_ptr; + playlist_t *playlist = playlist_get_cached(); if (playlist) playlist_get_index(playlist, idx, @@ -2797,7 +2781,7 @@ static int menu_displaylist_parse_horizontal_content_actions( && string_is_equal(menu->deferred_path, fullpath); if (content_loaded) - menu_displaylist_parse_load_content_settings(info); + menu_displaylist_parse_load_content_settings(info, menu); else { const char *ext = NULL; @@ -3202,16 +3186,12 @@ static int menu_displaylist_parse_options_cheats( } static int menu_displaylist_parse_options_remappings( - menu_displaylist_info_t *info) + menu_displaylist_info_t *info, menu_handle_t *menu) { unsigned p, retro_id; rarch_system_info_t *system = NULL; - menu_handle_t *menu = NULL; unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return -1; - for (p = 0; p < max_users; p++) { char key_type[PATH_MAX_LENGTH]; @@ -3779,21 +3759,24 @@ static void menu_displaylist_parse_playlist_associations( static bool menu_displaylist_push_internal( const char *label, menu_displaylist_ctx_entry_t *entry, - menu_displaylist_info_t *info) + menu_displaylist_info_t *info, + void *data) { + menu_handle_t *menu = (menu_handle_t*)data; + if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info)) + if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info, menu)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info)) + if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info, menu)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_SETTINGS_ALL, info)) + if (menu_displaylist_ctl(DISPLAYLIST_SETTINGS_ALL, info, menu)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB))) @@ -3812,7 +3795,7 @@ static bool menu_displaylist_push_internal( msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST)); menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - menu_displaylist_ctl(DISPLAYLIST_MUSIC_HISTORY, info); + menu_displaylist_ctl(DISPLAYLIST_MUSIC_HISTORY, info, menu); return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB))) @@ -3831,7 +3814,7 @@ static bool menu_displaylist_push_internal( msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST)); menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - menu_displaylist_ctl(DISPLAYLIST_VIDEO_HISTORY, info); + menu_displaylist_ctl(DISPLAYLIST_VIDEO_HISTORY, info, menu); return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB))) @@ -3861,7 +3844,7 @@ static bool menu_displaylist_push_internal( else info->need_push_no_playlist_entries = true; #endif - menu_displaylist_ctl(DISPLAYLIST_IMAGES_HISTORY, info); + menu_displaylist_ctl(DISPLAYLIST_IMAGES_HISTORY, info, menu); return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB))) @@ -3898,23 +3881,23 @@ static bool menu_displaylist_push_internal( info->path = strdup(settings->paths.directory_playlist); if (menu_displaylist_ctl( - DISPLAYLIST_DATABASE_PLAYLISTS, info)) + DISPLAYLIST_DATABASE_PLAYLISTS, info, menu)) return true; } } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_SCAN_DIRECTORY_LIST, info)) + if (menu_displaylist_ctl(DISPLAYLIST_SCAN_DIRECTORY_LIST, info, menu)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_NETPLAY_ROOM_LIST, info)) + if (menu_displaylist_ctl(DISPLAYLIST_NETPLAY_ROOM_LIST, info, menu)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU))) { - if (menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL, info)) + if (menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL, info, menu)) return true; } @@ -3955,7 +3938,7 @@ bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry) if (!info.list) goto error; - if (menu_displaylist_push_internal(label, entry, &info)) + if (menu_displaylist_push_internal(label, entry, &info, menu)) { ret = menu_displaylist_process(&info); goto end; @@ -4183,19 +4166,20 @@ void menu_displaylist_info_init(menu_displaylist_info_t *info) info->setting = NULL; } -bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) +bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data, + void *userdata) { size_t i; menu_ctx_displaylist_t disp_list; int ret = 0; core_info_list_t *list = NULL; - menu_handle_t *menu = NULL; bool load_content = true; bool use_filebrowser = false; menu_displaylist_info_t *info = (menu_displaylist_info_t*)data; + menu_handle_t *menu = (menu_handle_t*)userdata; settings_t *settings = config_get_ptr(); - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return false; core_info_get_list(&list); @@ -4273,7 +4257,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) } #ifdef HAVE_LIBRETRODB - ret = menu_displaylist_parse_database_entry(info); + ret = menu_displaylist_parse_database_entry(info, menu); #else ret = 0; #endif @@ -4391,13 +4375,13 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); if (string_is_equal(info->path, file_path_str(FILE_PATH_CONTENT_HISTORY))) { - if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info)) + if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info, menu)) return menu_displaylist_process(info); return false; } else if (string_is_equal(info->path, file_path_str(FILE_PATH_CONTENT_FAVORITES))) { - if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info)) + if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info, menu)) return menu_displaylist_process(info); return false; } @@ -6159,7 +6143,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_HORIZONTAL: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - ret = menu_displaylist_parse_horizontal_list(info); + ret = menu_displaylist_parse_horizontal_list(info, menu); info->need_sort = true; info->need_refresh = true; @@ -6167,13 +6151,13 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - ret = menu_displaylist_parse_horizontal_content_actions(info); + ret = menu_displaylist_parse_horizontal_content_actions(info, menu); info->need_refresh = true; info->need_push = true; break; case DISPLAYLIST_CONTENT_SETTINGS: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - ret = menu_displaylist_parse_load_content_settings(info); + ret = menu_displaylist_parse_load_content_settings(info, menu); info->need_refresh = true; info->need_push = true; @@ -6308,7 +6292,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_OPTIONS_REMAPPINGS: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - ret = menu_displaylist_parse_options_remappings(info); + ret = menu_displaylist_parse_options_remappings(info, menu); info->need_push = true; break; diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 7bcd105959..9614c11535 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -224,7 +224,8 @@ void menu_displaylist_info_free(menu_displaylist_info_t *info); void menu_displaylist_info_init(menu_displaylist_info_t *info); -bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data); +bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data, + void *userdata); #ifdef HAVE_NETWORKING void netplay_refresh_rooms_menu(file_list_t *list); #endif diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 9e8015c55b..3d25f3af27 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -1798,7 +1798,8 @@ static bool menu_driver_context_reset(bool video_is_threaded) { if (!menu_driver_ctx || !menu_driver_ctx->context_reset) return false; - menu_driver_ctx->context_reset(menu_userdata, video_is_threaded); + menu_driver_ctx->context_reset(menu_driver_data, + menu_userdata, video_is_threaded); return true; } diff --git a/menu/menu_driver.h b/menu/menu_driver.h index c9cbc48de9..84c07a58d4 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -443,7 +443,7 @@ typedef struct menu_ctx_driver * just toggled fullscreen, the GL driver did a teardown/setup - * we now need to rebuild all of our textures and state for the * menu driver. */ - void (*context_reset)(void *data, bool video_is_threaded); + void (*context_reset)(void *data, void *userdata, bool video_is_threaded); /* This will be invoked when we are running a hardware context * and the context in question wants to tear itself down. All * textures and related state on the menu driver will also diff --git a/menu/menu_setting.c b/menu/menu_setting.c index b785ed44b8..d1c46d6632 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -667,6 +667,7 @@ int menu_action_handle_setting(rarch_setting_t *setting, if (action == MENU_ACTION_OK) { menu_displaylist_info_t info; + menu_handle_t *menu = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); const char *name = setting->name; size_t selection = menu_navigation_get_selection(); @@ -679,7 +680,9 @@ int menu_action_handle_setting(rarch_setting_t *setting, info.directory_ptr = selection; info.list = menu_stack; - if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info)) + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info, menu)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); @@ -1372,6 +1375,7 @@ void general_write_handler(void *data) if (*setting->value.target.boolean) { menu_displaylist_info_t info; + menu_handle_t *menu = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); menu_displaylist_info_init(&info); @@ -1381,7 +1385,9 @@ void general_write_handler(void *data) msg_hash_to_str(MENU_ENUM_LABEL_HELP)); info.list = menu_stack; - if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info)) + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info, menu)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); setting_set_with_string_representation(setting, "false"); diff --git a/menu/widgets/menu_dialog.c b/menu/widgets/menu_dialog.c index 4157e31502..23aebda618 100644 --- a/menu/widgets/menu_dialog.c +++ b/menu/widgets/menu_dialog.c @@ -255,7 +255,8 @@ void menu_dialog_push_pending(bool push, enum menu_dialog_type type) void menu_dialog_push(void) { menu_displaylist_info_t info; - const char *label; + const char *label = NULL; + menu_handle_t *menu = NULL; if (!menu_dialog_is_push_pending()) return; @@ -270,7 +271,9 @@ void menu_dialog_push(void) if (label) info.label = strdup(label); - menu_displaylist_ctl(DISPLAYLIST_HELP, &info); + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + menu_displaylist_ctl(DISPLAYLIST_HELP, &info, menu); } void menu_dialog_set_current_id(unsigned id) diff --git a/menu/widgets/menu_input_bind_dialog.c b/menu/widgets/menu_input_bind_dialog.c index 55abf07785..2fc050aa01 100644 --- a/menu/widgets/menu_input_bind_dialog.c +++ b/menu/widgets/menu_input_bind_dialog.c @@ -86,6 +86,7 @@ static int menu_input_key_bind_set_mode_common( { menu_displaylist_info_t info; unsigned bind_type = 0; + menu_handle_t *menu = NULL; struct retro_keybind *keybind = NULL; unsigned index_offset = setting->index_offset; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); @@ -114,7 +115,10 @@ static int menu_input_key_bind_set_mode_common( info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND; info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND)); - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info, menu)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); break; @@ -130,7 +134,9 @@ static int menu_input_key_bind_set_mode_common( info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND_ALL)); - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info, menu)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); break; From f0285ef7920fdcb6cdc8f8111600e9c66bb5df3d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 21:27:57 +0200 Subject: [PATCH 251/517] (Menu) More refactors --- menu/cbs/menu_cbs_contentlist_switch.c | 5 +- menu/cbs/menu_cbs_left.c | 80 +++++++++++++++++--------- menu/cbs/menu_cbs_refresh.c | 6 +- menu/cbs/menu_cbs_right.c | 72 ++++++++++++++--------- menu/cbs/menu_cbs_select.c | 53 ++++++++++++----- menu/drivers/materialui.c | 3 +- menu/menu_cbs.h | 19 +++--- menu/menu_displaylist.c | 8 +-- menu/menu_displaylist.h | 2 +- menu/menu_entries.h | 12 ++-- menu/widgets/menu_entry.c | 6 +- 11 files changed, 172 insertions(+), 94 deletions(-) diff --git a/menu/cbs/menu_cbs_contentlist_switch.c b/menu/cbs/menu_cbs_contentlist_switch.c index 65d0af05ba..001047b865 100644 --- a/menu/cbs/menu_cbs_contentlist_switch.c +++ b/menu/cbs/menu_cbs_contentlist_switch.c @@ -22,13 +22,14 @@ cbs->action_content_list_switch_ident = #name; #endif -static int deferred_push_content_list(void *data, void *userdata, +static int deferred_push_content_list(void *data, + void *data2, void *userdata, const char *path, const char *label, unsigned type) { file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); menu_navigation_set_selection(0); - return action_refresh_default((file_list_t*)data, selection_buf); + return action_refresh_default(data, (file_list_t*)data2, selection_buf); } int menu_cbs_init_bind_content_list_switch(menu_file_list_cbs_t *cbs, diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 04a403ca2e..322d10ff0b 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -309,7 +309,8 @@ static int generic_shader_action_parameter_left( return 0; } -static int shader_action_parameter_left(unsigned type, const char *label, bool wraparound) +static int shader_action_parameter_left(void *data, + unsigned type, const char *label, bool wraparound) { video_shader_ctx_t shader_info; struct video_shader *shader = menu_shader_get(); @@ -333,7 +334,8 @@ static int shader_action_parameter_left(unsigned type, const char *label, bool w return ret; } -static int action_left_cheat(unsigned type, const char *label, +static int action_left_cheat(void *data, + unsigned type, const char *label, bool wraparound) { size_t idx = type - MENU_SETTINGS_CHEAT_BEGIN; @@ -341,8 +343,9 @@ static int action_left_cheat(unsigned type, const char *label, wraparound); } -static int action_left_input_desc(unsigned type, const char *label, - bool wraparound) +static int action_left_input_desc(void *data, + unsigned type, const char *label, + bool wraparound) { rarch_system_info_t *system = runloop_get_system_info(); settings_t *settings = config_get_ptr(); @@ -370,13 +373,14 @@ static int action_left_input_desc(unsigned type, const char *label, also skip all the axes until analog remapping is implemented */ if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) /*|| (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)*/) - action_left_input_desc(type, label, wraparound); + action_left_input_desc(data,type, label, wraparound); return 0; } -static int action_left_input_desc_kbd(unsigned type, const char *label, - bool wraparound) +static int action_left_input_desc_kbd(void *data, + unsigned type, const char *label, + bool wraparound) { unsigned remap_id; unsigned key_id, id, offset; @@ -411,7 +415,8 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, return 0; } -static int action_left_scroll(unsigned type, const char *label, +static int action_left_scroll(void *data, + unsigned type, const char *label, bool wraparound) { size_t scroll_accel = 0; @@ -439,14 +444,15 @@ static int action_left_scroll(unsigned type, const char *label, return 0; } -static int action_left_mainmenu(unsigned type, const char *label, +static int action_left_mainmenu(void *data, + unsigned type, const char *label, bool wraparound) { menu_ctx_list_t list_info; unsigned push_list = 0; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); menu_driver_ctl(RARCH_MENU_CTL_LIST_GET_SELECTION, &list_info); @@ -485,12 +491,12 @@ static int action_left_mainmenu(unsigned type, const char *label, menu_driver_ctl(RARCH_MENU_CTL_LIST_CACHE, &list_info); if (cbs && cbs->action_content_list_switch) - return cbs->action_content_list_switch( + return cbs->action_content_list_switch(menu, selection_buf, menu_stack, "", "", 0); } break; case 2: - action_left_scroll(0, "", false); + action_left_scroll(data, 0, "", false); break; case 0: default: @@ -500,7 +506,8 @@ static int action_left_mainmenu(unsigned type, const char *label, return 0; } -static int action_left_shader_scale_pass(unsigned type, const char *label, +static int action_left_shader_scale_pass(void *data, + unsigned type, const char *label, bool wraparound) { unsigned current_scale, delta; @@ -522,7 +529,8 @@ static int action_left_shader_scale_pass(unsigned type, const char *label, return 0; } -static int action_left_shader_filter_pass(unsigned type, const char *label, +static int action_left_shader_filter_pass(void *data, + unsigned type, const char *label, bool wraparound) { unsigned delta = 2; @@ -537,7 +545,8 @@ static int action_left_shader_filter_pass(unsigned type, const char *label, return 0; } -static int action_left_shader_filter_default(unsigned type, const char *label, +static int action_left_shader_filter_default(void *data, + unsigned type, const char *label, bool wraparound) { rarch_setting_t *setting = menu_setting_find_enum( @@ -548,7 +557,8 @@ static int action_left_shader_filter_default(unsigned type, const char *label, setting_get_type(setting), MENU_ACTION_LEFT, wraparound); } -static int action_left_cheat_num_passes(unsigned type, const char *label, +static int action_left_cheat_num_passes(void *data, + unsigned type, const char *label, bool wraparound) { bool refresh = false; @@ -563,7 +573,8 @@ static int action_left_cheat_num_passes(unsigned type, const char *label, return 0; } -static int action_left_shader_num_passes(unsigned type, const char *label, +static int action_left_shader_num_passes(void *data, + unsigned type, const char *label, bool wraparound) { bool refresh = false; @@ -583,7 +594,8 @@ static int action_left_shader_num_passes(unsigned type, const char *label, return 0; } -static int action_left_netplay_mitm_server(unsigned type, const char *label, +static int action_left_netplay_mitm_server(void *data, + unsigned type, const char *label, bool wraparound) { settings_t *settings = config_get_ptr(); @@ -600,13 +612,17 @@ static int action_left_netplay_mitm_server(unsigned type, const char *label, if (i - 1 >= 0) { found = true; - strlcpy(settings->arrays.netplay_mitm_server, netplay_mitm_server_list[i - 1].name, sizeof(settings->arrays.netplay_mitm_server)); + strlcpy(settings->arrays.netplay_mitm_server, + netplay_mitm_server_list[i - 1].name, + sizeof(settings->arrays.netplay_mitm_server)); break; } else if (wraparound) { found = true; - strlcpy(settings->arrays.netplay_mitm_server, netplay_mitm_server_list[list_len - 1].name, sizeof(settings->arrays.netplay_mitm_server)); + strlcpy(settings->arrays.netplay_mitm_server, + netplay_mitm_server_list[list_len - 1].name, + sizeof(settings->arrays.netplay_mitm_server)); break; } } @@ -615,13 +631,16 @@ static int action_left_netplay_mitm_server(unsigned type, const char *label, if (!found) { /* current entry was invalid, go back to the end */ - strlcpy(settings->arrays.netplay_mitm_server, netplay_mitm_server_list[list_len - 1].name, sizeof(settings->arrays.netplay_mitm_server)); + strlcpy(settings->arrays.netplay_mitm_server, + netplay_mitm_server_list[list_len - 1].name, + sizeof(settings->arrays.netplay_mitm_server)); } return 0; } -static int action_left_shader_watch_for_changes(unsigned type, const char *label, +static int action_left_shader_watch_for_changes(void *data, + unsigned type, const char *label, bool wraparound) { settings_t *settings = config_get_ptr(); @@ -629,14 +648,16 @@ static int action_left_shader_watch_for_changes(unsigned type, const char *label return 0; } -static int action_left_video_resolution(unsigned type, const char *label, +static int action_left_video_resolution(void *data, + unsigned type, const char *label, bool wraparound) { video_driver_get_prev_video_out(); return 0; } -static int playlist_association_left(unsigned type, const char *label, +static int playlist_association_left(void *data, + unsigned type, const char *label, bool wraparound) { unsigned i; @@ -699,7 +720,8 @@ static int playlist_association_left(unsigned type, const char *label, return 0; } -static int core_setting_left(unsigned type, const char *label, +static int core_setting_left(void *data, + unsigned type, const char *label, bool wraparound) { unsigned idx = type - MENU_SETTINGS_CORE_OPTION_START; @@ -709,14 +731,16 @@ static int core_setting_left(unsigned type, const char *label, return 0; } -static int disk_options_disk_idx_left(unsigned type, const char *label, +static int disk_options_disk_idx_left(void *data, + unsigned type, const char *label, bool wraparound) { command_event(CMD_EVENT_DISK_PREV, NULL); return 0; } -static int bind_left_generic(unsigned type, const char *label, +static int bind_left_generic(void *data, + unsigned type, const char *label, bool wraparound) { return menu_setting_set(type, label, MENU_ACTION_LEFT, wraparound); diff --git a/menu/cbs/menu_cbs_refresh.c b/menu/cbs/menu_cbs_refresh.c index 54881cfba6..87dba26f59 100644 --- a/menu/cbs/menu_cbs_refresh.c +++ b/menu/cbs/menu_cbs_refresh.c @@ -22,16 +22,18 @@ cbs->action_refresh_ident = #name; #endif -int action_refresh_default(file_list_t *list, file_list_t *menu_list) +int action_refresh_default(void *data, + file_list_t *list, file_list_t *menu_list) { menu_displaylist_ctx_entry_t entry; + menu_handle_t *menu = (menu_handle_t*)data; if (!menu_list) return -1; entry.list = list; entry.stack = menu_list; - if (!menu_displaylist_push(&entry)) + if (!menu_displaylist_push(&entry, menu)) return -1; return 0; } diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 0f2fa245a6..80572ebf28 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -43,7 +43,7 @@ #ifndef BIND_ACTION_RIGHT #define BIND_ACTION_RIGHT(cbs, name) \ do { \ - cbs->action_right = name; \ + cbs->action_right = name; \ cbs->action_right_ident = #name; \ } while(0) #endif @@ -61,7 +61,8 @@ static int generic_shader_action_parameter_right(struct video_shader_parameter * return 0; } -int shader_action_parameter_right(unsigned type, const char *label, bool wraparound) +int shader_action_parameter_right(void *data, + unsigned type, const char *label, bool wraparound) { video_shader_ctx_t shader_info; struct video_shader *shader = menu_shader_get(); @@ -93,7 +94,8 @@ int generic_action_cheat_toggle(size_t idx, unsigned type, const char *label, return 0; } -int action_right_cheat(unsigned type, const char *label, +int action_right_cheat(void *data, + unsigned type, const char *label, bool wraparound) { size_t idx = type - MENU_SETTINGS_CHEAT_BEGIN; @@ -101,7 +103,8 @@ int action_right_cheat(unsigned type, const char *label, wraparound); } -int action_right_input_desc_kbd(unsigned type, const char *label, +int action_right_input_desc_kbd(void *data, + unsigned type, const char *label, bool wraparound) { unsigned key_id, id, offset; @@ -140,7 +143,8 @@ int action_right_input_desc_kbd(unsigned type, const char *label, } /* fix-me: incomplete, lacks error checking */ -int action_right_input_desc(unsigned type, const char *label, +int action_right_input_desc(void *data, + unsigned type, const char *label, bool wraparound) { rarch_system_info_t *system = runloop_get_system_info(); @@ -166,7 +170,7 @@ int action_right_input_desc(unsigned type, const char *label, also skip all the axes until analog remapping is implemented */ if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) /*|| (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)*/) - action_right_input_desc(type, label, wraparound); + action_right_input_desc(data, type, label, wraparound); #if 0 int i = 0; @@ -178,7 +182,8 @@ int action_right_input_desc(unsigned type, const char *label, return 0; } -static int action_right_scroll(unsigned type, const char *label, +static int action_right_scroll(void *data, + unsigned type, const char *label, bool wraparound) { size_t scroll_accel = 0; @@ -207,7 +212,7 @@ static int action_right_scroll(unsigned type, const char *label, return 0; } -static int action_right_goto_tab(void) +static int action_right_goto_tab(menu_handle_t *menu) { menu_ctx_list_t list_info; file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); @@ -222,13 +227,14 @@ static int action_right_goto_tab(void) menu_driver_ctl(RARCH_MENU_CTL_LIST_CACHE, &list_info); if (cbs && cbs->action_content_list_switch) - return cbs->action_content_list_switch(selection_buf, menu_stack, + return cbs->action_content_list_switch(menu, selection_buf, menu_stack, "", "", 0); return 0; } -static int action_right_mainmenu(unsigned type, const char *label, +static int action_right_mainmenu(void *data, + unsigned type, const char *label, bool wraparound) { menu_ctx_list_t list_info; @@ -253,15 +259,16 @@ static int action_right_mainmenu(unsigned type, const char *label, if ((list_info.selection != (list_horiz_info.size + list_tabs_info.size)) || settings->bools.menu_navigation_wraparound_enable) - return action_right_goto_tab(); + return action_right_goto_tab((menu_handle_t*)data); } else - action_right_scroll(0, "", false); + action_right_scroll(data, 0, "", false); return 0; } -static int action_right_shader_scale_pass(unsigned type, const char *label, +static int action_right_shader_scale_pass(void *data, + unsigned type, const char *label, bool wraparound) { unsigned current_scale, delta; @@ -282,7 +289,8 @@ static int action_right_shader_scale_pass(unsigned type, const char *label, return 0; } -static int action_right_shader_filter_pass(unsigned type, const char *label, +static int action_right_shader_filter_pass(void *data, + unsigned type, const char *label, bool wraparound) { unsigned pass = type - MENU_SETTINGS_SHADER_PASS_FILTER_0; @@ -297,7 +305,8 @@ static int action_right_shader_filter_pass(unsigned type, const char *label, return 0; } -static int action_right_shader_filter_default(unsigned type, const char *label, +static int action_right_shader_filter_default(void *data, + unsigned type, const char *label, bool wraparound) { rarch_setting_t *setting = menu_setting_find_enum(MENU_ENUM_LABEL_VIDEO_SMOOTH); @@ -308,13 +317,13 @@ static int action_right_shader_filter_default(unsigned type, const char *label, wraparound); } -static int action_right_cheat_num_passes(unsigned type, const char *label, +static int action_right_cheat_num_passes(void *data, + unsigned type, const char *label, bool wraparound) { bool refresh = false; - unsigned new_size = 0; + unsigned new_size = cheat_manager_get_size() + 1; - new_size = cheat_manager_get_size() + 1; menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); cheat_manager_realloc(new_size); @@ -322,7 +331,8 @@ static int action_right_cheat_num_passes(unsigned type, const char *label, return 0; } -static int action_right_shader_num_passes(unsigned type, const char *label, +static int action_right_shader_num_passes(void *data, + unsigned type, const char *label, bool wraparound) { bool refresh = false; @@ -342,7 +352,8 @@ static int action_right_shader_num_passes(unsigned type, const char *label, return 0; } -static int action_right_netplay_mitm_server(unsigned type, const char *label, +static int action_right_netplay_mitm_server(void *data, + unsigned type, const char *label, bool wraparound) { settings_t *settings = config_get_ptr(); @@ -380,22 +391,26 @@ static int action_right_netplay_mitm_server(unsigned type, const char *label, return 0; } -static int action_right_shader_watch_for_changes(unsigned type, const char *label, +static int action_right_shader_watch_for_changes(void *data, + unsigned type, const char *label, bool wraparound) { settings_t *settings = config_get_ptr(); - settings->bools.video_shader_watch_files = !settings->bools.video_shader_watch_files; + settings->bools.video_shader_watch_files = + !settings->bools.video_shader_watch_files; return 0; } -static int action_right_video_resolution(unsigned type, const char *label, +static int action_right_video_resolution(void *data, + unsigned type, const char *label, bool wraparound) { video_driver_get_next_video_out(); return 0; } -static int playlist_association_right(unsigned type, const char *label, +static int playlist_association_right(void *data, + unsigned type, const char *label, bool wraparound) { char core_path[PATH_MAX_LENGTH]; @@ -454,7 +469,8 @@ static int playlist_association_right(unsigned type, const char *label, return 0; } -int core_setting_right(unsigned type, const char *label, +int core_setting_right(void *data, + unsigned type, const char *label, bool wraparound) { unsigned idx = type - MENU_SETTINGS_CORE_OPTION_START; @@ -464,7 +480,8 @@ int core_setting_right(unsigned type, const char *label, return 0; } -static int disk_options_disk_idx_right(unsigned type, const char *label, +static int disk_options_disk_idx_right(void *data, + unsigned type, const char *label, bool wraparound) { command_event(CMD_EVENT_DISK_NEXT, NULL); @@ -472,7 +489,8 @@ static int disk_options_disk_idx_right(unsigned type, const char *label, return 0; } -int bind_right_generic(unsigned type, const char *label, +int bind_right_generic(void *data, + unsigned type, const char *label, bool wraparound) { return menu_setting_set(type, label, MENU_ACTION_RIGHT, wraparound); diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 8d31efe823..6f3e821e87 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -120,47 +120,74 @@ static int action_select_path_use_directory(const char *path, path, label, type, idx, 0 /* unused */); } -static int action_select_driver_setting(const char *path, const char *label, unsigned type, +static int action_select_driver_setting(const char *path, + const char *label, unsigned type, size_t idx) { - return bind_right_generic(type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return bind_right_generic(menu, type, label, true); } -static int action_select_core_setting(const char *path, const char *label, unsigned type, +static int action_select_core_setting(const char *path, + const char *label, unsigned type, size_t idx) { - return core_setting_right(type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return core_setting_right(menu, type, label, true); } -static int shader_action_parameter_select(const char *path, const char *label, unsigned type, +static int shader_action_parameter_select(const char *path, + const char *label, unsigned type, size_t idx) { - return shader_action_parameter_right(type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return shader_action_parameter_right(menu, type, label, true); } -static int shader_action_parameter_preset_select(const char *path, const char *label, unsigned type, +static int shader_action_parameter_preset_select(const char *path, + const char *label, unsigned type, size_t idx) { - return shader_action_parameter_right(type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return shader_action_parameter_right(menu, type, label, true); } -static int action_select_cheat(const char *path, const char *label, unsigned type, +static int action_select_cheat(const char *path, + const char *label, unsigned type, size_t idx) { - return action_right_cheat(type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return action_right_cheat(menu, type, label, true); } -static int action_select_input_desc(const char *path, const char *label, unsigned type, +static int action_select_input_desc(const char *path, + const char *label, unsigned type, size_t idx) { - return action_right_input_desc(type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return action_right_input_desc(menu, type, label, true); } static int action_select_input_desc_kbd(const char *path, const char *label, unsigned type, size_t idx) { - return action_right_input_desc_kbd(type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return action_right_input_desc_kbd(menu, type, label, true); } #ifdef HAVE_NETWORKING diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 1e0c9344eb..c4b040e231 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -2354,7 +2354,8 @@ static int materialui_pointer_up(void *data, materialui_preswitch_tabs(mui, action); if (cbs && cbs->action_content_list_switch) - return cbs->action_content_list_switch(selection_buf, menu_stack, + return cbs->action_content_list_switch(data, + selection_buf, menu_stack, "", "", 0); } } diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index b5f14aa2c3..5083466d18 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -129,9 +129,11 @@ enum /* Function callbacks */ -int action_refresh_default(file_list_t *list, file_list_t *menu_list); +int action_refresh_default(void *data, + file_list_t *list, file_list_t *menu_list); -int shader_action_parameter_right(unsigned type, const char *label, bool wraparound); +int shader_action_parameter_right(void *data, + unsigned type, const char *label, bool wraparound); int generic_action_ok_displaylist_push(void *data, const char *path, const char *new_path, @@ -154,16 +156,16 @@ int generic_action_cheat_toggle(size_t idx, unsigned type, const char *label, bool wraparound); -int core_setting_right(unsigned type, const char *label, +int core_setting_right(void *data, unsigned type, const char *label, bool wraparound); -int action_right_input_desc(unsigned type, const char *label, +int action_right_input_desc(void *data, unsigned type, const char *label, bool wraparound); -int action_right_input_desc_kbd(unsigned type, const char *label, +int action_right_input_desc_kbd(void *data, unsigned type, const char *label, bool wraparound); -int action_right_cheat(unsigned type, const char *label, +int action_right_cheat(void *data, unsigned type, const char *label, bool wraparound); int setting_action_ok_video_refresh_rate_auto(void *data, bool wraparound); @@ -264,8 +266,9 @@ int action_scan_file(const char *path, const char *label, unsigned type, size_t idx); #endif -int bind_right_generic(unsigned type, const char *label, - bool wraparound); +int bind_right_generic(void *data, + unsigned type, const char *label, + bool wraparound); /* This sets up all the callback functions for a menu entry. * diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index e192883774..af25053750 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3904,7 +3904,7 @@ static bool menu_displaylist_push_internal( return false; } -bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry) +bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry, void *data) { menu_displaylist_info_t info; menu_file_list_cbs_t *cbs = NULL; @@ -3913,11 +3913,9 @@ bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry) unsigned type = 0; bool ret = false; enum msg_hash_enums enum_idx = MSG_UNKNOWN; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!entry) - return false; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!entry || !menu) return false; menu_displaylist_info_init(&info); diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 9614c11535..45c66069f0 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -218,7 +218,7 @@ typedef struct menu_displaylist_ctx_entry bool menu_displaylist_process(menu_displaylist_info_t *info); -bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry); +bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry, void *data); void menu_displaylist_info_free(menu_displaylist_info_t *info); diff --git a/menu/menu_entries.h b/menu/menu_entries.h index 57e5f1b604..069c3ead24 100644 --- a/menu/menu_entries.h +++ b/menu/menu_entries.h @@ -116,11 +116,15 @@ typedef struct menu_file_list_cbs size_t idx); int (*action_start)(unsigned type, const char *label); int (*action_info)(unsigned type, const char *label); - int (*action_content_list_switch)(void *data, void *userdata, const char + int (*action_content_list_switch)(void *data, + void *data2, void *userdata, const char *path, const char *label, unsigned type); - int (*action_left)(unsigned type, const char *label, bool wraparound); - int (*action_right)(unsigned type, const char *label, bool wraparound); - int (*action_refresh)(file_list_t *list, file_list_t *menu_list); + int (*action_left)(void *data, + unsigned type, const char *label, bool wraparound); + int (*action_right)(void *data, + unsigned type, const char *label, bool wraparound); + int (*action_refresh)(void *data, + file_list_t *list, file_list_t *menu_list); int (*action_up)(unsigned type, const char *label); int (*action_label)(file_list_t *list, unsigned type, unsigned i, diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index 6e6aea9007..85175600cd 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -466,11 +466,11 @@ int menu_entry_action(menu_entry_t *entry, break; case MENU_ACTION_LEFT: if (cbs && cbs->action_left) - ret = cbs->action_left(entry->type, entry->label, false); + ret = cbs->action_left(data, entry->type, entry->label, false); break; case MENU_ACTION_RIGHT: if (cbs && cbs->action_right) - ret = cbs->action_right(entry->type, entry->label, false); + ret = cbs->action_right(data, entry->type, entry->label, false); break; case MENU_ACTION_INFO: if (cbs && cbs->action_info) @@ -504,7 +504,7 @@ int menu_entry_action(menu_entry_t *entry, bool refresh = false; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); - cbs->action_refresh(selection_buf, menu_stack); + cbs->action_refresh(data, selection_buf, menu_stack); menu_entries_ctl(MENU_ENTRIES_CTL_UNSET_REFRESH, &refresh); } } From a8f7d99abaf3adefcee466013c035bd9fe4b0ec7 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 21:49:25 +0200 Subject: [PATCH 252/517] More cleanups - getting rid of RARCH_MENU_CTL_DRIVER_DATA_GET calls --- menu/cbs/menu_cbs_info.c | 13 +++--- menu/cbs/menu_cbs_left.c | 5 ++- menu/cbs/menu_cbs_ok.c | 2 +- menu/cbs/menu_cbs_right.c | 5 ++- menu/cbs/menu_cbs_select.c | 82 +++++++++++++++----------------------- menu/cbs/menu_cbs_start.c | 62 ++++++++++++++++++---------- menu/menu_entries.h | 7 ++-- menu/menu_setting.c | 15 +++---- menu/menu_setting.h | 9 +++-- menu/widgets/menu_entry.c | 6 +-- 10 files changed, 106 insertions(+), 100 deletions(-) diff --git a/menu/cbs/menu_cbs_info.c b/menu/cbs/menu_cbs_info.c index f6d673a212..bd880cd83d 100644 --- a/menu/cbs/menu_cbs_info.c +++ b/menu/cbs/menu_cbs_info.c @@ -30,10 +30,10 @@ #include "../../network/netplay/netplay_discovery.h" #endif -static int action_info_default(unsigned type, const char *label) +static int action_info_default(void *data, unsigned type, const char *label) { menu_displaylist_info_t info; - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); size_t selection = menu_navigation_get_selection(); @@ -45,8 +45,6 @@ static int action_info_default(unsigned type, const char *label) info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_INFO_SCREEN)); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - if (!menu_displaylist_ctl(DISPLAYLIST_HELP, &info, menu)) goto error; @@ -68,15 +66,14 @@ int generic_action_ok_help(void *data, const char *label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums id, enum menu_dialog_type id2); -static int action_info_cheevos(unsigned type, const char *label) +static int action_info_cheevos(void *data, + unsigned type, const char *label) { - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; unsigned new_id = type - MENU_SETTINGS_CHEEVOS_START; menu_dialog_set_current_id(new_id); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - return generic_action_ok_help(menu, NULL, label, new_id, 0, 0, MENU_ENUM_LABEL_CHEEVOS_DESCRIPTION, MENU_DIALOG_HELP_CHEEVOS_DESCRIPTION); diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 322d10ff0b..aa1d0c45f3 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -553,7 +553,7 @@ static int action_left_shader_filter_default(void *data, MENU_ENUM_LABEL_VIDEO_SMOOTH); if (!setting) return menu_cbs_exit(); - return menu_action_handle_setting(setting, + return menu_action_handle_setting(data, setting, setting_get_type(setting), MENU_ACTION_LEFT, wraparound); } @@ -743,7 +743,8 @@ static int bind_left_generic(void *data, unsigned type, const char *label, bool wraparound) { - return menu_setting_set(type, label, MENU_ACTION_LEFT, wraparound); + return menu_setting_set(data, + type, label, MENU_ACTION_LEFT, wraparound); } static int menu_cbs_init_bind_left_compare_label(menu_file_list_cbs_t *cbs, diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index a689c3ced4..d19808ae75 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -1761,7 +1761,7 @@ static int action_ok_lookup_setting(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return menu_setting_set(type, label, MENU_ACTION_OK, false); + return menu_setting_set(data, type, label, MENU_ACTION_OK, false); } static int action_ok_audio_add_to_mixer(void *data, diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 80572ebf28..e79fcc6809 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -312,7 +312,7 @@ static int action_right_shader_filter_default(void *data, rarch_setting_t *setting = menu_setting_find_enum(MENU_ENUM_LABEL_VIDEO_SMOOTH); if (!setting) return menu_cbs_exit(); - return menu_action_handle_setting(setting, + return menu_action_handle_setting(data, setting, setting_get_type(setting), MENU_ACTION_RIGHT, wraparound); } @@ -493,7 +493,8 @@ int bind_right_generic(void *data, unsigned type, const char *label, bool wraparound) { - return menu_setting_set(type, label, MENU_ACTION_RIGHT, wraparound); + return menu_setting_set(data, + type, label, MENU_ACTION_RIGHT, wraparound); } static int menu_cbs_init_bind_right_compare_type(menu_file_list_cbs_t *cbs, diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 6f3e821e87..6e5cf1d0b6 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -32,11 +32,12 @@ #ifndef BIND_ACTION_SELECT #define BIND_ACTION_SELECT(cbs, name) \ - cbs->action_select = name; \ + cbs->action_select = name; \ cbs->action_select_ident = #name; #endif -static int action_select_default(const char *path, const char *label, unsigned type, +static int action_select_default(void *data, + const char *path, const char *label, unsigned type, size_t idx) { menu_entry_t entry; @@ -44,6 +45,7 @@ static int action_select_default(const char *path, const char *label, unsigned t enum menu_action action = MENU_ACTION_NOOP; menu_file_list_cbs_t *cbs = NULL; file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + menu_handle_t *menu = (menu_handle_t*)data; menu_entry_init(&entry); menu_entry_get(&entry, 0, idx, NULL, false); @@ -95,13 +97,7 @@ static int action_select_default(const char *path, const char *label, unsigned t } if (action != MENU_ACTION_NOOP) - { - menu_handle_t *menu = NULL; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - ret = menu_entry_action(&entry, menu, (unsigned)idx, action); - } menu_entry_free(&entry); @@ -110,88 +106,76 @@ static int action_select_default(const char *path, const char *label, unsigned t return ret; } -static int action_select_path_use_directory(const char *path, +static int action_select_path_use_directory( + void *data, + const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return action_ok_path_use_directory(menu, + return action_ok_path_use_directory((menu_handle_t*)data, path, label, type, idx, 0 /* unused */); } -static int action_select_driver_setting(const char *path, +static int action_select_driver_setting(void *data, + const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return bind_right_generic(menu, type, label, true); + return bind_right_generic((menu_handle_t*)data, type, label, true); } -static int action_select_core_setting(const char *path, +static int action_select_core_setting(void *data, + const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return core_setting_right(menu, type, label, true); + return core_setting_right((menu_handle_t*)data, type, label, true); } -static int shader_action_parameter_select(const char *path, +static int shader_action_parameter_select(void *data, + const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return shader_action_parameter_right(menu, type, label, true); + return shader_action_parameter_right((menu_handle_t*)data, type, label, true); } -static int shader_action_parameter_preset_select(const char *path, +static int shader_action_parameter_preset_select( + void *data, const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return shader_action_parameter_right(menu, type, label, true); + return shader_action_parameter_right( + (menu_handle_t*)data, type, label, true); } -static int action_select_cheat(const char *path, +static int action_select_cheat(void *data, + const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return action_right_cheat(menu, type, label, true); + return action_right_cheat((menu_handle_t*)data, type, label, true); } -static int action_select_input_desc(const char *path, +static int action_select_input_desc(void *data, + const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return action_right_input_desc(menu, type, label, true); + return action_right_input_desc((menu_handle_t*)data, type, label, true); } -static int action_select_input_desc_kbd(const char *path, +static int action_select_input_desc_kbd(void *data, + const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return action_right_input_desc_kbd(menu, type, label, true); + return action_right_input_desc_kbd((menu_handle_t*)data, type, label, true); } #ifdef HAVE_NETWORKING -static int action_select_netplay_connect_room(const char *path, +static int action_select_netplay_connect_room( + void *data, + const char *path, const char *label, unsigned type, size_t idx) { diff --git a/menu/cbs/menu_cbs_start.c b/menu/cbs/menu_cbs_start.c index 399c49b490..b91093329e 100644 --- a/menu/cbs/menu_cbs_start.c +++ b/menu/cbs/menu_cbs_start.c @@ -45,17 +45,19 @@ #ifndef BIND_ACTION_START #define BIND_ACTION_START(cbs, name) \ - cbs->action_start = name; \ + cbs->action_start = name; \ cbs->action_start_ident = #name; #endif -static int action_start_remap_file_load(unsigned type, const char *label) +static int action_start_remap_file_load(void *data, + unsigned type, const char *label) { input_remapping_set_defaults(true); return 0; } -static int action_start_video_filter_file_load(unsigned type, const char *label) +static int action_start_video_filter_file_load(void *data, + unsigned type, const char *label) { settings_t *settings = config_get_ptr(); @@ -79,7 +81,8 @@ static int generic_action_start_performance_counters(struct retro_perf_counter * return 0; } -static int action_start_performance_counters_core(unsigned type, const char *label) +static int action_start_performance_counters_core(void *data, + unsigned type, const char *label) { struct retro_perf_counter **counters = retro_get_perf_counter_libretro(); unsigned offset = type - MENU_SETTINGS_LIBRETRO_PERF_COUNTERS_BEGIN; @@ -87,7 +90,8 @@ static int action_start_performance_counters_core(unsigned type, const char *lab return generic_action_start_performance_counters(counters, offset, type, label); } -static int action_start_performance_counters_frontend(unsigned type, +static int action_start_performance_counters_frontend( + void *data, unsigned type, const char *label) { struct retro_perf_counter **counters = retro_get_perf_counter_rarch(); @@ -95,7 +99,8 @@ static int action_start_performance_counters_frontend(unsigned type, return generic_action_start_performance_counters(counters, offset, type, label); } -static int action_start_input_desc(unsigned type, const char *label) +static int action_start_input_desc(void *data, + unsigned type, const char *label) { settings_t *settings = config_get_ptr(); unsigned inp_desc_index_offset = type - MENU_SETTINGS_INPUT_DESC_BEGIN; @@ -118,11 +123,12 @@ static int action_start_input_desc(unsigned type, const char *label) } static int action_start_shader_action_parameter( + void *data, unsigned type, const char *label) { video_shader_ctx_t shader_info; struct video_shader_parameter *param = NULL; - unsigned parameter = type - MENU_SETTINGS_SHADER_PARAMETER_0; + unsigned parameter = type - MENU_SETTINGS_SHADER_PARAMETER_0; video_shader_driver_get_current_shader(&shader_info); @@ -137,11 +143,12 @@ static int action_start_shader_action_parameter( return menu_shader_manager_clear_parameter(parameter); } -static int action_start_shader_pass(unsigned type, const char *label) +static int action_start_shader_pass(void *data, + unsigned type, const char *label) { - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return menu_cbs_exit(); menu->hack_shader_pass = type - MENU_SETTINGS_SHADER_PASS_0; @@ -152,7 +159,8 @@ static int action_start_shader_pass(unsigned type, const char *label) } -static int action_start_shader_scale_pass(unsigned type, const char *label) +static int action_start_shader_scale_pass(void *data, + unsigned type, const char *label) { unsigned pass = type - MENU_SETTINGS_SHADER_PASS_SCALE_0; @@ -161,32 +169,39 @@ static int action_start_shader_scale_pass(unsigned type, const char *label) return 0; } -static int action_start_shader_filter_pass(unsigned type, const char *label) +static int action_start_shader_filter_pass(void *data, + unsigned type, const char *label) { unsigned pass = type - MENU_SETTINGS_SHADER_PASS_FILTER_0; return menu_shader_manager_clear_pass_filter(pass); } -static int action_start_netplay_mitm_server(unsigned type, const char *label) +static int action_start_netplay_mitm_server(void *data, + unsigned type, const char *label) { settings_t *settings = config_get_ptr(); - strlcpy(settings->arrays.netplay_mitm_server, netplay_mitm_server, sizeof(settings->arrays.netplay_mitm_server)); + strlcpy(settings->arrays.netplay_mitm_server, + netplay_mitm_server, + sizeof(settings->arrays.netplay_mitm_server)); return 0; } -static int action_start_shader_watch_for_changes(unsigned type, const char *label) +static int action_start_shader_watch_for_changes(void *data, + unsigned type, const char *label) { settings_t *settings = config_get_ptr(); settings->bools.video_shader_watch_files = video_shader_watch_files; return 0; } -static int action_start_shader_num_passes(unsigned type, const char *label) +static int action_start_shader_num_passes(void *data, + unsigned type, const char *label) { return menu_shader_manager_clear_num_passes(); } -static int action_start_cheat_num_passes(unsigned type, const char *label) +static int action_start_cheat_num_passes(void *data, + unsigned type, const char *label) { if (cheat_manager_get_size()) { @@ -198,7 +213,8 @@ static int action_start_cheat_num_passes(unsigned type, const char *label) return 0; } -static int action_start_core_setting(unsigned type, +static int action_start_core_setting(void *data, + unsigned type, const char *label) { unsigned idx = type - MENU_SETTINGS_CORE_OPTION_START; @@ -210,7 +226,8 @@ static int action_start_core_setting(unsigned type, return 0; } -static int action_start_playlist_association(unsigned type, const char *label) +static int action_start_playlist_association(void *data, + unsigned type, const char *label) { int found; char new_playlist_cores[PATH_MAX_LENGTH]; @@ -245,7 +262,8 @@ static int action_start_playlist_association(unsigned type, const char *label) return 0; } -static int action_start_video_resolution(unsigned type, const char *label) +static int action_start_video_resolution(void *data, + unsigned type, const char *label) { unsigned width = 0, height = 0; global_t *global = global_get_ptr(); @@ -268,9 +286,9 @@ static int action_start_video_resolution(unsigned type, const char *label) return 0; } -static int action_start_lookup_setting(unsigned type, const char *label) +static int action_start_lookup_setting(void *data, unsigned type, const char *label) { - return menu_setting_set(type, label, MENU_ACTION_START, false); + return menu_setting_set(data, type, label, MENU_ACTION_START, false); } static int menu_cbs_init_bind_start_compare_label(menu_file_list_cbs_t *cbs) diff --git a/menu/menu_entries.h b/menu/menu_entries.h index 069c3ead24..8d64746ff2 100644 --- a/menu/menu_entries.h +++ b/menu/menu_entries.h @@ -103,7 +103,8 @@ typedef struct menu_file_list_cbs int (*action_iterate)(const char *label, unsigned action); int (*action_deferred_push)(menu_displaylist_info_t *info, void *data); - int (*action_select)(const char *path, const char *label, unsigned type, + int (*action_select)(void *data, + const char *path, const char *label, unsigned type, size_t idx); int (*action_get_title)(const char *path, const char *label, unsigned type, char *s, size_t len); @@ -114,8 +115,8 @@ typedef struct menu_file_list_cbs size_t idx); int (*action_scan)(const char *path, const char *label, unsigned type, size_t idx); - int (*action_start)(unsigned type, const char *label); - int (*action_info)(unsigned type, const char *label); + int (*action_start)(void *data, unsigned type, const char *label); + int (*action_info)(void *data, unsigned type, const char *label); int (*action_content_list_switch)(void *data, void *data2, void *userdata, const char *path, const char *label, unsigned type); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index d1c46d6632..335d75adc3 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -655,7 +655,8 @@ static int setting_handler(rarch_setting_t *setting, unsigned action) return -1; } -int menu_action_handle_setting(rarch_setting_t *setting, +int menu_action_handle_setting(void *data, + rarch_setting_t *setting, unsigned type, unsigned action, bool wraparound) { if (!setting) @@ -667,7 +668,6 @@ int menu_action_handle_setting(rarch_setting_t *setting, if (action == MENU_ACTION_OK) { menu_displaylist_info_t info; - menu_handle_t *menu = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); const char *name = setting->name; size_t selection = menu_navigation_get_selection(); @@ -680,9 +680,8 @@ int menu_action_handle_setting(rarch_setting_t *setting, info.directory_ptr = selection; info.list = menu_stack; - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, + &info, (menu_handle_t*)data)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); @@ -814,7 +813,8 @@ int menu_setting_set_flags(rarch_setting_t *setting) return 0; } -int menu_setting_set(unsigned type, const char *label, +int menu_setting_set(void *data, + unsigned type, const char *label, unsigned action, bool wraparound) { int ret = 0; @@ -826,7 +826,8 @@ int menu_setting_set(unsigned type, const char *label, if (!cbs) return 0; - ret = menu_action_handle_setting(cbs->setting, + ret = menu_action_handle_setting(data, + cbs->setting, type, action, wraparound); if (ret == -1) diff --git a/menu/menu_setting.h b/menu/menu_setting.h index 6fc32bdbfd..a0e6a98bee 100644 --- a/menu/menu_setting.h +++ b/menu/menu_setting.h @@ -76,7 +76,8 @@ int menu_setting_generic(rarch_setting_t *setting, bool wraparound); int menu_setting_set_flags(rarch_setting_t *setting); -int menu_setting_set(unsigned type, const char *label, +int menu_setting_set(void *data, + unsigned type, const char *label, unsigned action, bool wraparound); /** @@ -120,10 +121,12 @@ void menu_setting_get_label(void *data, char *s, size_t len, unsigned *w, unsigned type, const char *menu_label, const char *label, unsigned idx); -int menu_action_handle_setting(rarch_setting_t *setting, +int menu_action_handle_setting(void *data, + rarch_setting_t *setting, unsigned type, unsigned action, bool wraparound); -enum setting_type menu_setting_get_browser_selection_type(rarch_setting_t *setting); +enum setting_type menu_setting_get_browser_selection_type( + rarch_setting_t *setting); void *setting_get_ptr(rarch_setting_t *setting); diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index 85175600cd..e7f26d8f7f 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -462,7 +462,7 @@ int menu_entry_action(menu_entry_t *entry, break; case MENU_ACTION_START: if (cbs && cbs->action_start) - ret = cbs->action_start(entry->type, entry->label); + ret = cbs->action_start(data, entry->type, entry->label); break; case MENU_ACTION_LEFT: if (cbs && cbs->action_left) @@ -474,11 +474,11 @@ int menu_entry_action(menu_entry_t *entry, break; case MENU_ACTION_INFO: if (cbs && cbs->action_info) - ret = cbs->action_info(entry->type, entry->label); + ret = cbs->action_info(data, entry->type, entry->label); break; case MENU_ACTION_SELECT: if (cbs && cbs->action_select) - ret = cbs->action_select(entry->path, + ret = cbs->action_select(data, entry->path, entry->label, entry->type, i); break; case MENU_ACTION_SEARCH: From cb3b5d72abe87f95585f1aa460de5c97abdf407f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 21:53:44 +0200 Subject: [PATCH 253/517] (Menu) Get rid of more RARCH_MENU_CTL_DRIVER_DATA_GET calls --- menu/drivers/menu_generic.c | 2 +- menu/menu_input.c | 21 +++++---------------- menu/menu_input.h | 3 ++- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 2424c888bd..3add7400e2 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -254,7 +254,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) } if (BIT64_GET(menu->state, MENU_STATE_POST_ITERATE)) - menu_input_post_iterate(&ret, action); + menu_input_post_iterate(menu, &ret, action); end: if (ret) diff --git a/menu/menu_input.c b/menu/menu_input.c index a791ad83d3..a3929ce78f 100644 --- a/menu/menu_input.c +++ b/menu/menu_input.c @@ -210,6 +210,7 @@ static int menu_input_mouse_post_iterate(uint64_t *input_mouse, } static int menu_input_mouse_frame( + menu_handle_t *menu, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) { @@ -287,10 +288,6 @@ static int menu_input_mouse_frame( if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_BUTTON_R)) { size_t selection = menu_navigation_get_selection(); - menu_handle_t *menu = NULL; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - menu_entry_action(entry, menu, (unsigned)selection, MENU_ACTION_CANCEL); @@ -415,6 +412,7 @@ int16_t menu_input_mouse_state(enum menu_input_mouse_state state) } static int menu_input_pointer_post_iterate( + menu_handle_t *menu, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) { @@ -528,10 +526,6 @@ static int menu_input_pointer_post_iterate( if (menu_input->pointer.counter > 32) { size_t selection = menu_navigation_get_selection(); - menu_handle_t *menu = NULL; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - if (cbs && cbs->action_start) return menu_entry_action(entry, menu, (unsigned)selection, @@ -564,11 +558,7 @@ static int menu_input_pointer_post_iterate( { if (!pointer_oldback) { - menu_handle_t *menu = NULL; pointer_oldback = true; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - menu_entry_action(entry, menu, (unsigned)menu_navigation_get_selection(), MENU_ACTION_CANCEL); @@ -580,7 +570,7 @@ static int menu_input_pointer_post_iterate( return ret; } -void menu_input_post_iterate(int *ret, unsigned action) +void menu_input_post_iterate(void *data, int *ret, unsigned action) { menu_entry_t entry; settings_t *settings = config_get_ptr(); @@ -592,11 +582,10 @@ void menu_input_post_iterate(int *ret, unsigned action) menu_entry_init(&entry); menu_entry_get(&entry, 0, selection, NULL, false); - *ret = menu_input_mouse_frame(cbs, &entry, action); + *ret = menu_input_mouse_frame(data, cbs, &entry, action); if (settings->bools.menu_pointer_enable) - *ret |= menu_input_pointer_post_iterate(cbs, &entry, action); + *ret |= menu_input_pointer_post_iterate(data, cbs, &entry, action); menu_entry_free(&entry); } - diff --git a/menu/menu_input.h b/menu/menu_input.h index 2c5694eb00..1dd5dc669b 100644 --- a/menu/menu_input.h +++ b/menu/menu_input.h @@ -108,7 +108,8 @@ typedef struct menu_input_ctx_hitbox int32_t y2; } menu_input_ctx_hitbox_t; -void menu_input_post_iterate(int *ret, unsigned action); +void menu_input_post_iterate(void *data, + int *ret, unsigned action); int16_t menu_input_pointer_state(enum menu_input_pointer_state state); From 8632a925682d258a746b8921421140f733b06f2f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 21:59:36 +0200 Subject: [PATCH 254/517] Cleanups --- menu/widgets/menu_input_bind_dialog.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/menu/widgets/menu_input_bind_dialog.c b/menu/widgets/menu_input_bind_dialog.c index 2fc050aa01..b9ee32a6f7 100644 --- a/menu/widgets/menu_input_bind_dialog.c +++ b/menu/widgets/menu_input_bind_dialog.c @@ -81,12 +81,12 @@ static bool menu_input_key_bind_custom_bind_keyboard_cb( } static int menu_input_key_bind_set_mode_common( + menu_handle_t *menu, enum menu_input_binds_ctl_state state, rarch_setting_t *setting) { menu_displaylist_info_t info; unsigned bind_type = 0; - menu_handle_t *menu = NULL; struct retro_keybind *keybind = NULL; unsigned index_offset = setting->index_offset; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); @@ -116,8 +116,6 @@ static int menu_input_key_bind_set_mode_common( info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND)); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info, menu)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); @@ -134,8 +132,6 @@ static int menu_input_key_bind_set_mode_common( info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND_ALL)); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info, menu)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); @@ -263,7 +259,7 @@ bool menu_input_key_bind_set_mode( return false; if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return false; - if (menu_input_key_bind_set_mode_common(state, setting) == -1) + if (menu_input_key_bind_set_mode_common(menu, state, setting) == -1) return false; index_offset = setting->index_offset; From 8d6067b1d43b96028c61f9d710bfab7bba6c9295 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 22:03:29 +0200 Subject: [PATCH 255/517] Cleanups --- menu/widgets/menu_entry.c | 2 +- menu/widgets/menu_input_dialog.c | 10 +++++----- menu/widgets/menu_input_dialog.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index e7f26d8f7f..c4299fd107 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -482,7 +482,7 @@ int menu_entry_action(menu_entry_t *entry, entry->label, entry->type, i); break; case MENU_ACTION_SEARCH: - menu_input_dialog_start_search(); + menu_input_dialog_start_search(data); break; case MENU_ACTION_SCAN: diff --git a/menu/widgets/menu_input_dialog.c b/menu/widgets/menu_input_dialog.c index 1017c4c4d5..79af78a1a3 100644 --- a/menu/widgets/menu_input_dialog.c +++ b/menu/widgets/menu_input_dialog.c @@ -99,16 +99,16 @@ void menu_input_dialog_hide_kb(void) menu_input_dialog_keyboard_display = false; } -bool menu_input_dialog_start_search(void) +bool menu_input_dialog_start_search(void *data) { - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; - if (!menu_driver_ctl( - RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return false; menu_input_dialog_display_kb(); - strlcpy(menu_input_dialog_keyboard_label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), + strlcpy(menu_input_dialog_keyboard_label, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), sizeof(menu_input_dialog_keyboard_label)); input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_LINE_FREE, NULL); diff --git a/menu/widgets/menu_input_dialog.h b/menu/widgets/menu_input_dialog.h index 7cc048bb4f..cb6cc8e3ca 100644 --- a/menu/widgets/menu_input_dialog.h +++ b/menu/widgets/menu_input_dialog.h @@ -47,7 +47,7 @@ unsigned menu_input_dialog_get_kb_type(void); unsigned menu_input_dialog_get_kb_idx(void); -bool menu_input_dialog_start_search(void); +bool menu_input_dialog_start_search(void *data); void menu_input_dialog_hide_kb(void); From a3175a8fa3c4127b78582315f4082bcf8adf3bfc Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 10 Apr 2018 22:12:58 +0200 Subject: [PATCH 256/517] (Zarch) Buildfix --- menu/drivers/zarch.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/menu/drivers/zarch.c b/menu/drivers/zarch.c index 609341055f..444fe5861a 100644 --- a/menu/drivers/zarch.c +++ b/menu/drivers/zarch.c @@ -531,7 +531,11 @@ static int zarch_zui_render_lay_root_recent( tabbed->tabline_size + j * ZUI_ITEM_SIZE_PX, rich_label, i, entry_value, gamepad_index == (signed)i)) { - if (menu_entry_action(&entry, i, MENU_ACTION_OK)) + menu_handle_t *menu = (menu_handle_t*)NULL; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (menu_entry_action(&entry, menu, i, MENU_ACTION_OK)) { menu_entry_free(&entry); if (!string_is_empty(rich_label)) @@ -1084,7 +1088,7 @@ static bool zarch_load_image(void *userdata, &zui->textures.bg); break; case MENU_IMAGE_THUMBNAIL: - break; + case MENU_IMAGE_LEFT_THUMBNAIL: case MENU_IMAGE_SAVESTATE_THUMBNAIL: /* TODO/FIXME -implement */ break; @@ -1093,10 +1097,10 @@ static bool zarch_load_image(void *userdata, return true; } -static void zarch_context_reset(void *data, bool is_threaded) +static void zarch_context_reset(void *data, void *userdata, bool is_threaded) { settings_t *settings = config_get_ptr(); - zui_t *zui = (zui_t*)data; + zui_t *zui = (zui_t*)userdata; if (!zui || !settings) return; @@ -1118,6 +1122,7 @@ static int zarch_iterate(void *data, void *userdata, enum menu_action action) { int ret; menu_entry_t entry; + menu_handle_t *menu = (menu_handle_t*)data; zui_t *zui = (zui_t*)userdata; size_t selection = menu_navigation_get_selection(); @@ -1129,7 +1134,7 @@ static int zarch_iterate(void *data, void *userdata, enum menu_action action) zui->action = action; - ret = menu_entry_action(&entry, selection, action); + ret = menu_entry_action(data, &entry, selection, action); menu_entry_free(&entry); if (ret) return -1; @@ -1156,11 +1161,11 @@ static bool zarch_menu_init_list(void *data) info.list = selection_buf; - if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, &info)) + if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, &info, data)) { - bool ret = false; + bool ret = false; info.need_push = true; - ret = menu_displaylist_process(&info); + ret = menu_displaylist_process(&info); menu_displaylist_info_free(&info); return ret; } From 7da45bc78981bfc24063f769bec99e7a85955120 Mon Sep 17 00:00:00 2001 From: i30817 Date: Wed, 11 Apr 2018 01:37:35 +0100 Subject: [PATCH 257/517] Serial magic should only match on full match As was mentioned on the wii dual layer disc issue, this line is comparing binary sequences as strings, which fails very obviously because neither of the 'strings' tested are strings and it's very likely both start with \0. --- tasks/task_database_cue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/task_database_cue.c b/tasks/task_database_cue.c index fa53bcb264..e6360d4d30 100644 --- a/tasks/task_database_cue.c +++ b/tasks/task_database_cue.c @@ -381,7 +381,7 @@ int detect_system(intfstream_t *fd, const char **system_name) if (read < MAGIC_LEN) continue; - if (string_is_equal(MAGIC_NUMBERS[i].magic, magic)) + if (memcmp(MAGIC_NUMBERS[i].magic, magic, MAGIC_LEN) == 0) { *system_name = MAGIC_NUMBERS[i].system_name; rv = 0; From f481924f1e9b3a59211df2cc99d094daf0a7ae3f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 03:04:58 +0200 Subject: [PATCH 258/517] (Menu) Update menu code --- menu/cbs/menu_cbs_ok.c | 4 ++-- menu/drivers/menu_generic.c | 2 +- menu/widgets/menu_dialog.c | 18 ++---------------- menu/widgets/menu_dialog.h | 5 +---- menu/widgets/menu_input_dialog.c | 10 ++++++---- menu/widgets/menu_input_dialog.h | 3 ++- network/netplay/netplay_handshake.c | 8 +++++++- setting_list.c | 5 ++++- 8 files changed, 25 insertions(+), 30 deletions(-) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index d19808ae75..a4eb7b1395 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -2020,7 +2020,7 @@ static int (funcname)(void *data, const char *path, const char *label, unsigned line.type = type; \ line.idx = (_idx); \ line.cb = _cb; \ - if (!menu_input_dialog_start(&line)) \ + if (!menu_input_dialog_start(&line, data)) \ return -1; \ return 0; \ } @@ -3986,7 +3986,7 @@ static int action_ok_netplay_enable_client(void *data, line.idx = 0; line.cb = action_ok_netplay_enable_client_hostname_cb; - if (menu_input_dialog_start(&line)) + if (menu_input_dialog_start(&line, data)) return 0; #endif return -1; diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 3add7400e2..aa091bdd10 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -238,7 +238,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); /* Have to defer it so we let settings refresh. */ - menu_dialog_push(); + menu_dialog_push(menu); } break; } diff --git a/menu/widgets/menu_dialog.c b/menu/widgets/menu_dialog.c index 23aebda618..0c42557094 100644 --- a/menu/widgets/menu_dialog.c +++ b/menu/widgets/menu_dialog.c @@ -252,7 +252,7 @@ void menu_dialog_push_pending(bool push, enum menu_dialog_type type) menu_dialog_active = true; } -void menu_dialog_push(void) +void menu_dialog_push(void *data) { menu_displaylist_info_t info; const char *label = NULL; @@ -271,9 +271,7 @@ void menu_dialog_push(void) if (label) info.label = strdup(label); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - menu_displaylist_ctl(DISPLAYLIST_HELP, &info, menu); + menu_displaylist_ctl(DISPLAYLIST_HELP, &info, data); } void menu_dialog_set_current_id(unsigned id) @@ -291,18 +289,6 @@ void menu_dialog_reset(void) menu_display_toggle_set_reason(MENU_TOGGLE_REASON_NONE); } -void menu_dialog_show_message( - enum menu_dialog_type type, enum msg_hash_enums msg) -{ - menu_dialog_current_msg = msg; - - if (!menu_driver_ctl(RARCH_MENU_CTL_IS_TOGGLE, NULL)) - menu_display_toggle_set_reason(MENU_TOGGLE_REASON_MESSAGE); - - menu_dialog_push_pending(true, type); - menu_dialog_push(); -} - bool menu_dialog_is_active(void) { return menu_dialog_active; diff --git a/menu/widgets/menu_dialog.h b/menu/widgets/menu_dialog.h index b692fe3138..0df01e8202 100644 --- a/menu/widgets/menu_dialog.h +++ b/menu/widgets/menu_dialog.h @@ -59,13 +59,10 @@ void menu_dialog_unset_pending_push(void); bool menu_dialog_is_push_pending(void); -void menu_dialog_push(void); +void menu_dialog_push(void *data); void menu_dialog_reset(void); -void menu_dialog_show_message( - enum menu_dialog_type type, enum msg_hash_enums msg); - bool menu_dialog_is_active(void); void menu_dialog_set_current_id(unsigned id); diff --git a/menu/widgets/menu_input_dialog.c b/menu/widgets/menu_input_dialog.c index 79af78a1a3..ca33931fd9 100644 --- a/menu/widgets/menu_input_dialog.c +++ b/menu/widgets/menu_input_dialog.c @@ -119,12 +119,13 @@ bool menu_input_dialog_start_search(void *data) return true; } -bool menu_input_dialog_start(menu_input_ctx_line_t *line) +bool menu_input_dialog_start(menu_input_ctx_line_t *line, + void *data) { - menu_handle_t *menu = NULL; + menu_handle_t *menu = (menu_handle_t*)data; if (!line) return false; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if (!menu) return false; menu_input_dialog_display_kb(); @@ -135,7 +136,8 @@ bool menu_input_dialog_start(menu_input_ctx_line_t *line) sizeof(menu_input_dialog_keyboard_label)); if (line->label_setting) strlcpy(menu_input_dialog_keyboard_label_setting, - line->label_setting, sizeof(menu_input_dialog_keyboard_label_setting)); + line->label_setting, + sizeof(menu_input_dialog_keyboard_label_setting)); menu_input_dialog_keyboard_type = line->type; menu_input_dialog_keyboard_idx = line->idx; diff --git a/menu/widgets/menu_input_dialog.h b/menu/widgets/menu_input_dialog.h index cb6cc8e3ca..0c4b8187a0 100644 --- a/menu/widgets/menu_input_dialog.h +++ b/menu/widgets/menu_input_dialog.h @@ -55,7 +55,8 @@ void menu_input_dialog_display_kb(void); bool menu_input_dialog_get_display_kb(void); -bool menu_input_dialog_start(menu_input_ctx_line_t *line); +bool menu_input_dialog_start(menu_input_ctx_line_t *line, + void *data); void menu_input_dialog_end(void); diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c index 021b51d35c..48ceedfbf0 100644 --- a/network/netplay/netplay_handshake.c +++ b/network/netplay/netplay_handshake.c @@ -37,6 +37,7 @@ #include "../../version.h" #ifdef HAVE_MENU +#include "../../menu/menu_driver.h" #include "../../menu/widgets/menu_input_dialog.h" #endif @@ -415,6 +416,8 @@ bool netplay_handshake_init(netplay_t *netplay, { #ifdef HAVE_MENU menu_input_ctx_line_t line; + menu_handle_t *menu = NULL; + rarch_menu_running(); #endif @@ -425,7 +428,10 @@ bool netplay_handshake_init(netplay_t *netplay, line.label = msg_hash_to_str(MSG_NETPLAY_ENTER_PASSWORD); line.label_setting = "no_setting"; line.cb = handshake_password; - if (!menu_input_dialog_start(&line)) + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (menu && !menu_input_dialog_start(&line, menu)) return false; #endif } diff --git a/setting_list.c b/setting_list.c index 254bd62e38..eb7047360d 100644 --- a/setting_list.c +++ b/setting_list.c @@ -2004,6 +2004,7 @@ static void menu_input_st_hex_cb(void *userdata, const char *str) static int setting_generic_action_ok_linefeed(void *data, bool wraparound) { menu_input_ctx_line_t line; + menu_handle_t *menu = NULL; input_keyboard_line_complete_t cb = NULL; rarch_setting_t *setting = (rarch_setting_t*)data; @@ -2034,7 +2035,9 @@ static int setting_generic_action_ok_linefeed(void *data, bool wraparound) line.idx = 0; line.cb = cb; - if (!menu_input_dialog_start(&line)) + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (!menu || !menu_input_dialog_start(&line, menu)) return -1; return 0; From 40d45c94513f7032399f3298835e4e25218392ae Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 04:03:07 +0200 Subject: [PATCH 259/517] Manually merge PT BR --- intl/msg_hash_pt_br.h | 1769 +++++++++++++++++++++-------------------- 1 file changed, 915 insertions(+), 854 deletions(-) diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index fea530b8ba..6ed40a144f 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -11,16 +11,16 @@ MSG_HASH(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED, "Comando Netplay desconhecido recebido" ) MSG_HASH(MSG_FILE_ALREADY_EXISTS_SAVING_TO_BACKUP_BUFFER, - "Este arquivo já existe. Salvando no buffer de backup" + "Este arquivo j existe. Salvando no buffer de backup" ) MSG_HASH(MSG_GOT_CONNECTION_FROM, - "Conexão recebida de: \"%s\"" + "Conexo recebida de: \"%s\"" ) MSG_HASH(MSG_GOT_CONNECTION_FROM_NAME, - "Conexão recebida de: \"%s (%s)\"" + "Conexo recebida de: \"%s (%s)\"" ) MSG_HASH(MSG_PUBLIC_ADDRESS, - "Endereço público" + "Endereo pblico" ) MSG_HASH(MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "Nenhum argumento fornecido e nenhum menu interno, exibindo ajuda..." @@ -32,16 +32,52 @@ MSG_HASH(MSG_WAITING_FOR_CLIENT, "Aguardando pelo cliente..." ) MSG_HASH(MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME, - "Você deixou o jogo" + "Voc deixou o jogo" ) MSG_HASH(MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, - "Você se juntou como jogador %u" + "Voc se juntou como jogador %u" ) +MSG_HASH( + MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S, + "Voc se juntou aos dispositivos de entrada %.*s" + ) +MSG_HASH( + MSG_NETPLAY_PLAYER_S_LEFT, + "O jogador %.*s deixou o jogo" + ) +MSG_HASH( + MSG_NETPLAY_S_HAS_JOINED_AS_PLAYER_N, + "%.*s se juntou como jogador %u" + ) +MSG_HASH( + MSG_NETPLAY_S_HAS_JOINED_WITH_INPUT_DEVICES_S, + "%.*s juntou-se a dispositivos de entrada %.*s" + ) +MSG_HASH( + MSG_NETPLAY_NOT_RETROARCH, + "Uma tentativa de conexo com o netplay falhou porque o par no est executando o RetroArch ou est executando uma verso antiga do RetroArch." + ) +MSG_HASH( + MSG_NETPLAY_OUT_OF_DATE, + "O par netplay est executando uma verso antiga do RetroArch. No pode conectar." + ) +MSG_HASH( + MSG_NETPLAY_DIFFERENT_VERSIONS, + "ATENO: Um par de Netplay est executando uma verso diferente do RetroArch. Se ocorrerem problemas, use a mesma verso." + ) +MSG_HASH( + MSG_NETPLAY_DIFFERENT_CORES, + "Um par de netplay est executando um ncleo diferente. No pode conectar." + ) +MSG_HASH( + MSG_NETPLAY_DIFFERENT_CORE_VERSIONS, + "ATENO: Um par de Netplay est executando uma verso diferente do ncleo. Se ocorrerem problemas, use a mesma verso." + ) MSG_HASH(MSG_NETPLAY_ENDIAN_DEPENDENT, - "Este núcleo não suporta Netplay inter-arquitetura entre estes sistemas" + "Este ncleo no suporta Netplay inter-arquitetura entre estes sistemas" ) MSG_HASH(MSG_NETPLAY_PLATFORM_DEPENDENT, - "Este núcleo não suporta Netplay inter-arquitetura" + "Este ncleo no suporta Netplay inter-arquitetura" ) MSG_HASH(MSG_NETPLAY_ENTER_PASSWORD, "Digite a senha do servidor de Netplay:" @@ -59,13 +95,13 @@ MSG_HASH(MSG_NETPLAY_CLIENT_HANGUP, "Desconectado do Netplay" ) MSG_HASH(MSG_NETPLAY_CANNOT_PLAY_UNPRIVILEGED, - "Você não tem permissão para jogar" + "Voc no tem permisso para jogar" ) MSG_HASH(MSG_NETPLAY_CANNOT_PLAY_NO_SLOTS, - "Não há vagas livres para jogadores" + "No h vagas livres para jogadores" ) MSG_HASH(MSG_NETPLAY_CANNOT_PLAY, - "Impossível alterar para modo jogador" + "Impossvel alterar para modo jogador" ) MSG_HASH(MSG_NETPLAY_PEER_PAUSED, "Par do Netplay \"%s\" pausou" @@ -74,19 +110,19 @@ MSG_HASH(MSG_NETPLAY_CHANGED_NICK, "Seu apelido mudou para \"%s\"" ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHARED_CONTEXT, - "Dar aos núcleos renderizados por hardware seu próprio contexto privado. Evita ter que assumir mudanças de estado de hardware entre quadros." + "Dar aos ncleos renderizados por hardware seu prprio contexto privado. Evita ter que assumir mudanas de estado de hardware entre quadros." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SETTINGS, - "Ajusta as configurações de aparência da tela de menu." + "Ajusta as configuraes de aparncia da tela de menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC, - "Sincronia rígida entre CPU e GPU. Reduz a latência ao custo de desempenho." + "Sincronia rgida entre CPU e GPU. Reduz a latncia ao custo de desempenho." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_THREADED, - "Melhora o desempenho ao custo de latência e mais engasgamento de vídeo. Use somente se você não puder obter velocidade total de outra forma." + "Melhora o desempenho ao custo de latncia e mais engasgamento de vdeo. Use somente se voc no puder obter velocidade total de outra forma." ) MSG_HASH(MSG_AUDIO_VOLUME, - "Volume de áudio" + "Volume de udio" ) MSG_HASH(MSG_AUTODETECT, "Autodetectar" @@ -104,10 +140,10 @@ MSG_HASH(MSG_CONNECTING_TO_PORT, "Conectando a porta" ) MSG_HASH(MSG_CONNECTION_SLOT, - "Vaga de conexão" + "Vaga de conexo" ) MSG_HASH(MSG_SORRY_UNIMPLEMENTED_CORES_DONT_DEMAND_CONTENT_NETPLAY, - "Desculpe, não implementado: núcleos que não exigem conteúdo não podem participar do Netplay." + "Desculpe, no implementado: ncleos que no exigem contedo no podem participar do Netplay." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_PASSWORD, "Senha" @@ -116,7 +152,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_SETTINGS, "Contas Cheevos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_USERNAME, - "Nome de usuário" + "Nome de usurio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ACCOUNTS_LIST, "Contas" @@ -134,13 +170,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_LIST_HARDCORE, "Lista de Conquistas (Hardcore)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_CONTENT_LIST, - "Analisar Conteúdo" + "Analisar Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIGURATIONS_LIST, - "Configurações" + "Configuraes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TAB, - "Importar conteúdo" + "Importar contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_TAB, "Salas de Netplay" @@ -155,49 +191,49 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_BLOCK_FRAMES, "Bloquear Quadros" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_DEVICE, - "Dispositivo de Áudio" + "Dispositivo de udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_DRIVER, - "Driver de Áudio" + "Driver de udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_DSP_PLUGIN, - "Plugin DSP de Áudio" + "Plugin DSP de udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE, - "Habilitar Áudio" + "Habilitar udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_FILTER_DIR, - "Filtro de Áudio" + "Filtro de udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_TURBO_DEADZONE_LIST, "Turbo/Zona-Morta" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_LATENCY, - "Latência de Áudio (ms)" + "Latncia de udio (ms)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MAX_TIMING_SKEW, - "Desvio Máximo de Tempo do Áudio" + "Desvio Mximo de Tempo do udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MUTE, - "Áudio Mudo" + "udio Mudo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_OUTPUT_RATE, - "Taxa da Saída de Áudio (Hz)" + "Taxa da Sada de udio (Hz)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_RATE_CONTROL_DELTA, - "Controle Dinâmico da Taxa de Áudio" + "Controle Dinmico da Taxa de udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_DRIVER, - "Driver de Reamostragem de Áudio" + "Driver de Reamostragem de udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_SETTINGS, - "Áudio" + "udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_SYNC, - "Sincronizar Áudio" + "Sincronizar udio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_VOLUME, - "Nível de Volume de Áudio (dB)" + "Nvel de Volume de udio (dB)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_WASAPI_EXCLUSIVE_MODE, "WASAPI Modo Exclusivo" @@ -212,13 +248,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTOSAVE_INTERVAL, "Intervalo do Autossalvamento da SRAM" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTO_OVERRIDES_ENABLE, - "Autocarregar Arquivos de Redefinição" + "Autocarregar Arquivos de Redefinio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTO_REMAPS_ENABLE, "Autocarregar Arquivos de Remapeamento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTO_SHADERS_ENABLE, - "Autocarregar Predefinições de Shader" + "Autocarregar Predefinies de Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_BACK, "Voltar" @@ -227,7 +263,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_CONFIRM, "Confirmar" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_INFO, - "Informações" + "Informaes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_QUIT, "Sair" @@ -248,13 +284,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_TOGGLE_MENU, "Alternar Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS, - "Controles Básicos de Menu" + "Controles Bsicos de Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_CONFIRM, "Confirmar/OK" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_INFO, - "Informação" + "Informao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_QUIT, "Sair" @@ -263,7 +299,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_SCROLL_UP, "Rolar para Cima" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_START, - "Padrões" + "Padres" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_TOGGLE_KEYBOARD, "Alternar Teclado" @@ -272,7 +308,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_TOGGLE_MENU, "Alternar Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BLOCK_SRAM_OVERWRITE, - "Não sobregravar a SRAM ao carregar Estado de Jogo" + "No sobregravar a SRAM ao carregar Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BLUETOOTH_ENABLE, "Habilitar Bluetooth" @@ -284,45 +320,45 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CACHE_DIRECTORY, "Cache" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CAMERA_ALLOW, - "Permitir Câmera" + "Permitir Cmera" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CAMERA_DRIVER, - "Driver de Câmera" + "Driver de Cmera" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT, - "Trapaça" + "Trapaa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_APPLY_CHANGES, - "Aplicar Alterações" + "Aplicar Alteraes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_DATABASE_PATH, - "Arquivo de Trapaça" + "Arquivo de Trapaa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_FILE, - "Arquivo de Trapaça" + "Arquivo de Trapaa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_FILE_LOAD, - "Carregar Arquivo de Trapaça" + "Carregar Arquivo de Trapaa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_FILE_SAVE_AS, - "Salvar Arquivo de Trapaça Como" + "Salvar Arquivo de Trapaa Como" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_NUM_PASSES, - "Estágios de Trapaça" + "Estgios de Trapaa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_DESCRIPTION, - "Descrição" + "Descrio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, "Conquistas no Modo Hardcore" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_LEADERBOARDS_ENABLE, - "Tabelas de Classificação" + "Tabelas de Classificao" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_BADGES_ENABLE, - "Insígnias de Conquistas" + "Insgnias de Conquistas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ACHIEVEMENTS, "Conquistas Bloqueadas:" @@ -334,67 +370,77 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_SETTINGS, "Retro Achievements" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_TEST_UNOFFICIAL, - "Testar Conquistas Não Oficiais" + "Testar Conquistas No Oficiais" ) -MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_VERBOSE_ENABLE, - "Modo Detalhado das Conquistas" - ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ACHIEVEMENTS, "Conquistas Desbloqueadas:" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY, "Desbloqueada" ) -MSG_HASH(MENU_ENUM_LABEL_VALUE_CLOSE_CONTENT, - "Fechar Conteúdo" +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY_HARDCORE, + "Hardcore" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CHEEVOS_VERBOSE_ENABLE, + "Conquistas Modo Verboso" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CHEEVOS_AUTO_SCREENSHOT, + "Captura de Conquistas Automtica" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CLOSE_CONTENT, + "Fechar Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIG, - "Configuração" + "Configurao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIGURATIONS, - "Carregar Configuração" + "Carregar Configurao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIGURATION_SETTINGS, - "Configuração" + "Configurao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIG_SAVE_ON_EXIT, - "Salvar Configuração ao Sair" + "Salvar Configurao ao Sair" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_COLLECTION_LIST, - "Coleções" + "Colees" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_DATABASE_DIRECTORY, "Base de Dados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_DIR, - "Conteúdo" + "Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_HISTORY_SIZE, - "Tamanho da Lista de Histórico" + "Tamanho da Lista de Histrico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_ENTRY_REMOVE, - "Permitir a remoção de itens" + "Permitir a remoo de itens" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SETTINGS, - "Menu Rápido" + "Menu Rpido" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ASSETS_DIR, - "Recursos de Núcleo" + "Recursos de Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ASSETS_DIRECTORY, "Downloads" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_CHEAT_OPTIONS, - "Trapaças" + "Trapaas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_COUNTERS, - "Contadores do Núcleo" + "Contadores do Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ENABLE, - "Exibir nome do núcleo" + "Exibir nome do ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFORMATION, - "Informação do Núcleo" + "Informao do Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_AUTHORS, "Autores" @@ -403,22 +449,22 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CATEGORIES, "Categorias" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_LABEL, - "Rótulo do núcleo" + "Rtulo do ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_NAME, - "Nome do núcleo" + "Nome do ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_FIRMWARE, "Firmware(s)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_LICENSES, - "Licença(s)" + "Licena(s)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_PERMISSIONS, - "Permissões" + "Permisses" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_SUPPORTED_EXTENSIONS, - "Extensões suportadas" + "Extenses suportadas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_MANUFACTURER, "Fabricante do sistema" @@ -430,25 +476,25 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INPUT_REMAPPING_OPTIONS, "Controles" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_LIST, - "Carregar Núcleo" + "Carregar Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_OPTIONS, - "Opções" + "Opes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_SETTINGS, - "Núcleo" + "Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_SET_SUPPORTS_NO_CONTENT_ENABLE, - "Iniciar um Núcleo Automaticamente" + "Iniciar um Ncleo Automaticamente" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE, "Extrair automaticamente o arquivo baixado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_UPDATER_BUILDBOT_URL, - "URL de Núcleos do Buildbot" + "URL de Ncleos do Buildbot" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_UPDATER_LIST, - "Atualizador de Núcleo" + "Atualizador de Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_UPDATER_SETTINGS, "Atualizador" @@ -466,79 +512,79 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CURSOR_MANAGER, "Gerenciar Cursor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CUSTOM_RATIO, - "Proporção Personalizada" + "Proporo Personalizada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_MANAGER, "Gerenciar Base de Dados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_SELECTION, - "Seleção de Base de Dados" + "Seleo de Base de Dados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DELETE_ENTRY, "Remover" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FAVORITES, - "Diretório Inicial" + "Diretrio Inicial" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_CONTENT, - "" + "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_DEFAULT, - "" + "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_NONE, "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_NOT_FOUND, - "Diretório não encontrado." + "Diretrio no encontrado." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_SETTINGS, - "Diretório" + "Diretrio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_CYCLE_TRAY_STATUS, - "Condição da Bandeja do Ciclo de Disco" + "Condio da Bandeja do Ciclo de Disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_IMAGE_APPEND, "Anexar Imagem de Disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_INDEX, - "Índice de Disco" + "ndice de Disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_OPTIONS, "Controle de Disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DONT_CARE, - "Não importa" + "No importa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST, "Downloads" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE, - "Baixar Núcleo..." + "Baixar Ncleo..." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE_CONTENT, - "Download de Conteúdo" + "Download de Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DPI_OVERRIDE_ENABLE, - "Habilitar Redefinição de DPI" + "Habilitar Redefinio de DPI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DPI_OVERRIDE_VALUE, - "Redefinição de DPI" + "Redefinio de DPI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DRIVER_SETTINGS, "Driver" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DUMMY_ON_CORE_SHUTDOWN, - "Carregar Modelo no Desligamento do Núcleo" + "Carregar Modelo no Desligamento do Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHECK_FOR_MISSING_FIRMWARE, "Verificar por Firmware que Falta Antes de Carregar" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DYNAMIC_WALLPAPER, - "Plano de Fundo Dinâmico" + "Plano de Fundo Dinmico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DYNAMIC_WALLPAPERS_DIRECTORY, - "Planos de Fundo Dinâmicos" + "Planos de Fundo Dinmicos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_ENABLE, "Habilitar Conquistas" @@ -553,7 +599,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_FALSE, "Falso" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FASTFORWARD_RATIO, - "Velocidade Máxima de Execução" + "Velocidade Mxima de Execuo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FAVORITES_TAB, "Favoritos" @@ -562,7 +608,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_FPS_SHOW, "Mostrar Taxa de Quadros" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_ENABLE, - "Controlar Velocidade Máxima de Execução" + "Controlar Velocidade Mxima de Execuo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_SETTINGS, "Controle de Quadros" @@ -571,43 +617,43 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_FRONTEND_COUNTERS, "Contadores do Frontend" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS, - "Autocarregar Opções de Núcleo Específicas do Conteúdo" + "Autocarregar Opes de Ncleo Especficas do Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE, - "Criar arquivo de opções do jogo" + "Criar arquivo de opes do jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE, - "Arquivo de opções do jogo" + "Arquivo de opes do jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP, "Ajuda" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_AUDIO_VIDEO_TROUBLESHOOTING, - "Solução de Problemas de Áudio/Vídeo" + "Soluo de Problemas de udio/Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_CHANGE_VIRTUAL_GAMEPAD, - "Alterando a Transparência de Gamepad Virtual" + "Alterando a Transparncia de Gamepad Virtual" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_CONTROLS, - "Controles Básicos de Menu" + "Controles Bsicos de Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_LIST, "Ajuda" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_LOADING_CONTENT, - "Carregando Conteúdo" + "Carregando Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_SCANNING_CONTENT, - "Procurando em Busca de Conteúdo" + "Procurando em Busca de Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_WHAT_IS_A_CORE, - "O Que É Um Núcleo?" + "O Que Um Ncleo?" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HISTORY_LIST_ENABLE, - "Habilitar Lista de Histórico" + "Habilitar Lista de Histrico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HISTORY_TAB, - "Histórico" + "Histrico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HORIZONTAL_MENU, "Menu Horizontal" @@ -616,52 +662,52 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_IMAGES_TAB, "Imagem" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INFORMATION, - "Informação" + "Informao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INFORMATION_LIST, - "Informação" + "Informao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ADC_TYPE, - "Tipo de Analógico Para Digital" + "Tipo de Analgico Para Digital" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ALL_USERS_CONTROL_MENU, - "Todos os Usuários Controlam o Menu" + "Todos os Usurios Controlam o Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X, - "Analógico Esquerdo X" + "Analgico Esquerdo X" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_MINUS, - "Analógico Esquerdo X- (esquerda)" + "Analgico Esquerdo X- (esquerda)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_PLUS, - "Analógico Esquerdo X+ (direita)" + "Analgico Esquerdo X+ (direita)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y, - "Analógico Esquerdo Y" + "Analgico Esquerdo Y" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_MINUS, - "Analógico Esquerdo Y- (cima)" + "Analgico Esquerdo Y- (cima)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_PLUS, - "Analógico Esquerdo Y+ (baixo)" + "Analgico Esquerdo Y+ (baixo)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X, - "Analógico Direito X" + "Analgico Direito X" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X_MINUS, - "Analógico Direito X- (esquerda)" + "Analgico Direito X- (esquerda)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X_PLUS, - "Analógico Direito X+ (direita)" + "Analgico Direito X+ (direita)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y, - "Analógico Direito Y" + "Analgico Direito Y" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_MINUS, - "Analógico Direito Y- (cima)" + "Analgico Direito Y- (cima)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_PLUS, - "Analógico Direito Y+ (baixo)" + "Analgico Direito Y+ (baixo)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_TRIGGER, "Gatinho da Pistola") @@ -686,37 +732,37 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_LEFT, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_RIGHT, "D-pad Direito da Pistola") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_AUTODETECT_ENABLE, - "Habilitar Autoconfiguração" + "Habilitar Autoconfigurao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_AXIS_THRESHOLD, - "Zona Morta do Controle Analógico" + "Zona Morta do Controle Analgico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_INPUT_SWAP_OK_CANCEL, - "Inverter Botões OK e Cancelar do Menu" + "Inverter Botes OK e Cancelar do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_ALL, "Vincular Todos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_DEFAULT_ALL, - "Vincular Todos pelo Padrão" + "Vincular Todos pelo Padro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_TIMEOUT, "Tempo Limite para Vincular" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DESCRIPTOR_HIDE_UNBOUND, - "Ocultar Descritores de Entrada do Núcleo Não Vinculados" + "Ocultar Descritores de Entrada do Ncleo No Vinculados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DESCRIPTOR_LABEL_SHOW, - "Exibir Rótulos do Descritor de Entrada" + "Exibir Rtulos do Descritor de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_INDEX, - "Índice de Dispositivo" + "ndice de Dispositivo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_TYPE, "Tipo de Dispositivo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_INDEX, - "Índice de Mouse" + "ndice de Mouse" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DRIVER, "Driver de Entrada" @@ -725,58 +771,58 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DUTY_CYCLE, "Ciclo de Trabalho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_HOTKEY_BINDS, - "Vínculos das Teclas de Atalho da Entrada" + "Vnculos das Teclas de Atalho da Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ICADE_ENABLE, "Habilitar Mapeamento de Gamepad no Teclado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_A, - "Botão A (direita)" + "Boto A (direita)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_B, - "Botão B (baixo)" + "Boto B (baixo)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_DOWN, "Direcional para baixo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_L2, - "Botão L2 (gatilho)" + "Boto L2 (gatilho)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_L3, - "Botão L3 (polegar)" + "Boto L3 (polegar)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_L, - "Botão L (ombro)" + "Boto L (ombro)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_LEFT, "Direcional Esquerdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_R2, - "Botão R2 (gatilho)" + "Boto R2 (gatilho)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_R3, - "Botão R3 (polegar)" + "Boto R3 (polegar)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_R, - "Botão R (ombro)" + "Boto R (ombro)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_RIGHT, "Direcional Direito" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_SELECT, - "Botão Select" + "Boto Select" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_START, - "Botão Start" + "Boto Start" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_UP, "Direcional para Cima" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_X, - "Botão X (topo)" + "Boto X (topo)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_Y, - "Botão Y (esquerda)" + "Boto Y (esquerda)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_KEY, "(Tecla: %s)" @@ -803,25 +849,25 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_KEYBOARD_GAMEPAD_MAPPING_TYPE, "Tipo de Mapeamento para Gamepad no Teclado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_MAX_USERS, - "Usuários Máximos" + "Usurios Mximos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_MENU_ENUM_TOGGLE_GAMEPAD_COMBO, - "Combinação do Gamepad para Alternar Menu" + "Combinao do Gamepad para Alternar Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_CHEAT_INDEX_MINUS, - "Índice de Trapaça -" + "ndice de Trapaa -" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_CHEAT_INDEX_PLUS, - "Índice de Trapaça +" + "ndice de Trapaa +" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_CHEAT_TOGGLE, - "Alternar Trapaça" + "Alternar Trapaa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_EJECT_TOGGLE, - "Alternar ejeção de disco" + "Alternar ejeo de disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_NEXT, - "Próximo disco" + "Prximo disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_PREV, "Disco anterior" @@ -830,13 +876,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_ENABLE_HOTKEY, "Habilitar teclas de atalho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_FAST_FORWARD_HOLD_KEY, - "Manter Avanço Rápido" + "Manter Avano Rpido" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_FAST_FORWARD_KEY, - "Alternar Avanço Rápido" + "Alternar Avano Rpido" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_FRAMEADVANCE, - "Avanço de Quadro" + "Avano de Quadro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_FULLSCREEN_TOGGLE_KEY, "Alternar tela cheia" @@ -854,10 +900,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, "Alternar menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, - "Alternar gravação de filme" + "Alternar gravao de filme" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, - "Alternar áudio mudo" + "Alternar udio mudo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Alternar modo jogador/espectador do Netplay" @@ -866,7 +912,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, "Alternar teclado virtual" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OVERLAY_NEXT, - "Próxima Transparência" + "Prxima Transparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE, "Alternar pausa" @@ -887,13 +933,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SCREENSHOT, "Capturar tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_NEXT, - "Próximo Shader" + "Prximo Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_PREV, "Shader Anterior" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION_HOLD_KEY, - "Câmera Lenta" + "Cmera Lenta" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, "Compartimento do Estado de Jogo -" @@ -908,13 +954,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_VOLUME_UP, "Volume +" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_ENABLE, - "Mostrar Transparência" + "Mostrar Transparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_HIDE_IN_MENU, - "Ocultar Transparência no Menu" + "Ocultar Transparncia no Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS, - "Exibir Comandos Na Transparência" + "Exibir Comandos Na Transparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS_PORT, "Porta de Escuta do Exibir Comandos " @@ -938,10 +984,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_REMAPPING_DIRECTORY, "Remapeamento de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_REMAP_BINDS_ENABLE, - "Habilitar Remapeamento de Vínculos" + "Habilitar Remapeamento de Vnculos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_SAVE_AUTOCONFIG, - "Salvar Autoconfiguração" + "Salvar Autoconfigurao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_SETTINGS, "Entrada" @@ -956,61 +1002,61 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_TURBO_ENABLE, "Habilitar Turbo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_TURBO_PERIOD, - "Período do Turbo" + "Perodo do Turbo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_USER_BINDS, - "Vínculos de Entrada do Usuário %u" + "Vnculos de Entrada do Usurio %u" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INTERNAL_STORAGE_STATUS, - "Condição do armazenamento interno" + "Condio do armazenamento interno" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_JOYPAD_AUTOCONFIG_DIR, - "Autoconfiguração de Entrada" + "Autoconfigurao de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_JOYPAD_DRIVER, "Driver de Joypad" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LAKKA_SERVICES, - "Serviços" + "Servios" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_CHINESE_SIMPLIFIED, - "Chinês (Simplificado)" + "Chins (Simplificado)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_CHINESE_TRADITIONAL, - "Chinês (Tradicional)" + "Chins (Tradicional)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_DUTCH, - "Holandês" + "Holands" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ENGLISH, - "Inglês" + "Ingls" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ESPERANTO, "Esperanto" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_FRENCH, - "Francês" + "Francs" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_GERMAN, - "Alemão" + "Alemo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ITALIAN, "Italiano" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_JAPANESE, - "Japonês" + "Japons" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_KOREAN, "Coreano" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_POLISH, - "Polonês" + "Polons" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_PORTUGUESE_BRAZIL, - "Português (Brasil)" + "Portugus (Brasil)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_PORTUGUESE_PORTUGAL, - "Português (Portugal)" + "Portugus (Portugal)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_RUSSIAN, "Russo" @@ -1026,16 +1072,16 @@ MSG_HASH( "Arabic" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LEFT_ANALOG, - "Analógico Esquerdo" + "Analgico Esquerdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LIBRETRO_DIR_PATH, - "Núcleo" + "Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LIBRETRO_INFO_PATH, - "Informação do Núcleo" + "Informao do Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LIBRETRO_LOG_LEVEL, - "Nível de Registro de Eventos do Núcleo" + "Nvel de Registro de Eventos do Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LINEAR, "Linear" @@ -1047,16 +1093,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_HISTORY, "Carregar Recente" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_LIST, - "Carregar Conteúdo" + "Carregar Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOAD_STATE, "Carregar Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOCATION_ALLOW, - "Permitir Localização" + "Permitir Localizao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOCATION_DRIVER, - "Driver de Localização" + "Driver de Localizao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOGGING_SETTINGS, "Registro de Eventos" @@ -1068,7 +1114,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MAIN_MENU, "Menu Principal" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MANAGEMENT, - "Configurações da Base de Dados" + "Configuraes da Base de Dados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME, "Tema de Cor do Menu" @@ -1095,10 +1141,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_YELLOW, "Amarelo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_FOOTER_OPACITY, - "Opacidade do Rodapé" + "Opacidade do Rodap" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_HEADER_OPACITY, - "Opacidade do Cabeçalho" + "Opacidade do Cabealho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_DRIVER, "Driver de Menu" @@ -1107,16 +1153,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_ENUM_THROTTLE_FRAMERATE, "Controlar Taxa de Quadros do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_FILE_BROWSER_SETTINGS, - "Configurações" + "Configuraes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_LINEAR_FILTER, "Filtro Linear de Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_HORIZONTAL_ANIMATION, - "Animação Horizontal" + "Animao Horizontal" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SETTINGS, - "Aparência" + "Aparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_WALLPAPER, "Plano de Fundo" @@ -1134,19 +1180,19 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MOUSE_ENABLE, "Suporte para Mouse" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MULTIMEDIA_SETTINGS, - "Multimídia" + "Multimdia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MUSIC_TAB, - "Música" + "Msica" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE, - "Filtrar Extensões Desconhecidas" + "Filtrar Extenses Desconhecidas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NAVIGATION_WRAPAROUND, - "Navegação Retorna ao Início" + "Navegao Retorna ao Incio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NEAREST, - "Mais Próximo" + "Mais Prximo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY, "Netplay" @@ -1158,10 +1204,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES, "Verificar Quadros do Netplay" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, - "Quadros de Latência de Entrada" + "Quadros de Latncia de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, - "Faixa de Quadros de Latência de Entrada" + "Faixa de Quadros de Latncia de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Atraso de Quadros do Netplay" @@ -1182,7 +1228,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISABLE_HOST, "Parar hospedeiro de Netplay" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_IP_ADDRESS, - "Endereço do Servidor" + "Endereo do Servidor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_LAN_SCAN_SETTINGS, "Analisar a rede local" @@ -1191,7 +1237,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_MODE, "Habilitar Cliente Netplay" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_NICKNAME, - "Usuário" + "Usurio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD, "Senha do Servidor" @@ -1201,16 +1247,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE, MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUEST_DEVICE_I, "Solicitar Dispositivo %u") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUIRE_SLAVES, - "Não Permitir Clientes em Modo Não Escravo" + "No Permitir Clientes em Modo No Escravo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS, - "Configurações do Netplay") + "Configuraes do Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG, - "Compartilhamento de Entrada Analógica") + "Compartilhamento de Entrada Analgica") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_MAX, - "Máximo") + "Mximo") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_AVERAGE, - "Médio") + "Mdio") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL, "Compartilhamento de Entrada Digital") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_OR, @@ -1222,7 +1268,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_VOTE, MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NONE, "Nenhum") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NO_PREFERENCE, - "Sem preferência") + "Sem preferncia") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR, "Modo Espectador do Netplay" ) @@ -1248,7 +1294,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_PORT, "Porta de Comando de Rede" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_INFORMATION, - "Informação de Rede" + "Informao de Rede" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_REMOTE_ENABLE, "Gamepad de Rede" @@ -1260,7 +1306,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_SETTINGS, "Rede" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO, - "Não" + "No" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NONE, "Nenhum" @@ -1269,28 +1315,28 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE, "N/D" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_ACHIEVEMENTS_TO_DISPLAY, - "Não há Conquistas para mostrar." + "No h Conquistas para mostrar." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_CORE, - "Nenhum Núcleo" + "Nenhum Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_CORES_AVAILABLE, - "Nenhum núcleo disponível" + "Nenhum ncleo disponvel" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_CORE_INFORMATION_AVAILABLE, - "Não há informação de núcleo disponível." + "No h informao de ncleo disponvel." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_CORE_OPTIONS_AVAILABLE, - "Não há opções de núcleo disponíveis." + "No h opes de ncleo disponveis." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY, - "Não há itens para mostrar." + "No h itens para mostrar." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_HISTORY_AVAILABLE, - "Não há histórico disponível." + "No h histrico disponvel." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE, - "Não há informação disponível." + "No h informao disponvel." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_ITEMS, "Sem itens." @@ -1302,19 +1348,19 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_NETWORKS_FOUND, "Nenhuma rede encontrada." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_PERFORMANCE_COUNTERS, - "Não há contadores de desempenho." + "No h contadores de desempenho." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_PLAYLISTS, - "Não há listas de reprodução." + "No h listas de reproduo." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_PLAYLIST_ENTRIES_AVAILABLE, - "Não há itens de lista de reprodução disponíveis." + "No h itens de lista de reproduo disponveis." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND, - "Nenhuma configuração encontrada." + "Nenhuma configurao encontrada." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_SHADER_PARAMETERS, - "Não há parâmetros de Shader." + "No h parmetros de Shader." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OFF, "DESLIGADO" @@ -1329,13 +1375,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ONLINE_UPDATER, "Atualizador Online" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ONSCREEN_DISPLAY_SETTINGS, - "Exibição na Tela" + "Exibio na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ONSCREEN_OVERLAY_SETTINGS, - "Transparência na Tela" + "Transparncia na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ONSCREEN_NOTIFICATIONS_SETTINGS, - "Notificações na Tela" + "Notificaes na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OPEN_ARCHIVE, "Navegar no Arquivo" @@ -1344,49 +1390,49 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_OPTIONAL, "Opcional" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY, - "Transparência" + "Transparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_AUTOLOAD_PREFERRED, - "Autocarregar Transparência Favorita" + "Autocarregar Transparncia Favorita" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_DIRECTORY, - "Transparência" + "Transparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_OPACITY, - "Opacidade da Transparência" + "Opacidade da Transparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_PRESET, - "Predefinição de Transparência" + "Predefinio de Transparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_SCALE, - "Escala da Transparência" + "Escala da Transparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_SETTINGS, - "Transparência na Tela" + "Transparncia na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PAL60_ENABLE, "Utilizar Modo PAL60" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PARENT_DIRECTORY, - "Diretório superior" + "Diretrio superior" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PAUSE_LIBRETRO, "Pausar quando o menu for ativado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PAUSE_NONACTIVE, - "Não rodar em segundo plano" + "No rodar em segundo plano" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PERFCNT_ENABLE, "Contadores de Desempenho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB, - "Listas de Reprodução" + "Listas de Reproduo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_DIRECTORY, - "Lista de Reprodução" + "Lista de Reproduo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_SETTINGS, - "Listas de Reprodução" + "Listas de Reproduo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_POINTER_ENABLE, "Suporte para Toque" @@ -1404,13 +1450,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_QUIT_RETROARCH, "Sair do RetroArch" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ANALOG, - "Analógico suportado" + "Analgico suportado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_BBFC_RATING, - "Classificação BBFC" + "Classificao BBFC" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CERO_RATING, - "Classificação CERO" + "Classificao CERO" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_COOP, "Cooperativo suportado" @@ -1419,37 +1465,37 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CRC32, "CRC32" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DESCRIPTION, - "Descrição" + "Descrio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DEVELOPER, "Desenvolvedor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_EDGE_MAGAZINE_ISSUE, - "Edição da Revista Edge" + "Edio da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_EDGE_MAGAZINE_RATING, - "Classificação da Revista Edge" + "Classificao da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_EDGE_MAGAZINE_REVIEW, - "Análise da Revista Edge" + "Anlise da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ELSPA_RATING, - "Classificação ELSPA" + "Classificao ELSPA" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ENHANCEMENT_HW, "Hardware de Aprimoramento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ESRB_RATING, - "Classificação ESRB" + "Classificao ESRB" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_FAMITSU_MAGAZINE_RATING, - "Classificação da Revista Famitsu" + "Classificao da Revista Famitsu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_FRANCHISE, "Franquia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_GENRE, - "Gênero" + "Gnero" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_MD5, "MD5" @@ -1461,58 +1507,58 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ORIGIN, "Origem" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PEGI_RATING, - "Classificação PEGI" + "Classificao PEGI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PUBLISHER, "Editor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_RELEASE_MONTH, - "Mês de Lançamento" + "Ms de Lanamento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_RELEASE_YEAR, - "Ano de Lançamento" + "Ano de Lanamento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_RUMBLE, - "Suporte para Vibração" + "Suporte para Vibrao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_SERIAL, - "Número de Série" + "Nmero de Srie" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_SHA1, "SHA1" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_START_CONTENT, - "Iniciar Conteúdo" + "Iniciar Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_TGDB_RATING, - "Classificação TGDB" + "Classificao TGDB" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REBOOT, "Reiniciar" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORDING_CONFIG_DIRECTORY, - "Configuração de Gravação" + "Configurao de Gravao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORDING_OUTPUT_DIRECTORY, - "Saída de Gravação" + "Sada de Gravao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORDING_SETTINGS, - "Gravação" + "Gravao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_CONFIG, - "Carregar Configuração de Gravação..." + "Carregar Configurao de Gravao..." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_DRIVER, - "Driver de Gravação" + "Driver de Gravao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_ENABLE, - "Habilitar Gravação" + "Habilitar Gravao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_PATH, - "Salvar Saída de Gravação Como..." + "Salvar Sada de Gravao Como..." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_USE_OUTPUT_DIRECTORY, - "Salvar Gravações no Diretório de Saída" + "Salvar Gravaes no Diretrio de Sada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE, "Arquivo de Remapeamento" @@ -1521,19 +1567,19 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_LOAD, "Carregar Arquivo de Remapeamento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_SAVE_CORE, - "Salvar Arquivo de Remapeamento de Núcleo" + "Salvar Arquivo de Remapeamento de Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_SAVE_GAME, "Salvar Arquivo de Remapeamento de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_REMOVE_CORE, - "Remover Arquivo de Remapeamento de Núcleo" + "Remover Arquivo de Remapeamento de Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_REMOVE_GAME, "Remover Arquivo de Remapeamento de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REQUIRED, - "Obrigatório" + "Obrigatrio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RESTART_CONTENT, "Reiniciar" @@ -1554,31 +1600,31 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROPAD, "RetroPad" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROPAD_WITH_ANALOG, - "RetroPad com Analógico" + "RetroPad com Analgico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RETRO_ACHIEVEMENTS_SETTINGS, "Conquistas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_ENABLE, - "Habilitar Voltar Atrás" + "Habilitar Voltar Atrs" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_GRANULARITY, - "Granularidade do Voltar Atrás" + "Granularidade do Voltar Atrs" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_SETTINGS, - "Voltar Atrás" + "Voltar Atrs" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RGUI_BROWSER_DIRECTORY, "Navegador de Arquivos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RGUI_CONFIG_DIRECTORY, - "Configuração" + "Configurao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RGUI_SHOW_START_SCREEN, "Mostrar Tela Inicial" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RIGHT_ANALOG, - "Analógico Direito" + "Analgico Direito" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_FAVORITES, "Adicionar aos Favoritos" @@ -1599,7 +1645,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVEFILE_DIRECTORY, "Arquivo de Jogo-Salvo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATE_AUTO_INDEX, - "Índice Automático de Estado de Jogo" + "ndice Automtico de Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATE_AUTO_LOAD, "Autocarregar Estado de Jogo" @@ -1614,16 +1660,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATE_THUMBNAIL_ENABLE, "Miniaturas do Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG, - "Salvar Configuração Atual" + "Salvar Configurao Atual" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG_OVERRIDE_CORE, - "Salvar Redefinição de Núcleo" + "Salvar Redefinio de Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG_OVERRIDE_GAME, - "Salvar Redefinição de Jogo" + "Salvar Redefinio de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_NEW_CONFIG, - "Salvar Nova Configuração" + "Salvar Nova Configurao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_STATE, "Salvar Estado de Jogo" @@ -1632,19 +1678,19 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVING_SETTINGS, "Salvando" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCAN_DIRECTORY, - "Analisar Diretório" + "Analisar Diretrio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCAN_FILE, "Analisar Arquivo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCAN_THIS_DIRECTORY, - "" + "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCREENSHOT_DIRECTORY, "Captura de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCREEN_RESOLUTION, - "Resolução da Tela" + "Resoluo da Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SEARCH, "Procurar" @@ -1653,16 +1699,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SECONDS, "segundos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SETTINGS, - "Configurações" + "Configuraes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SETTINGS_TAB, - "Configurações" + "Configuraes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER, "Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_APPLY_CHANGES, - "Aplicar Alterações" + "Aplicar Alteraes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS, "Shaders" @@ -1680,7 +1726,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_PIPELINE_SNOW, "Neve" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHOW_ADVANCED_SETTINGS, - "Exibir Configurações Avançadas" + "Exibir Configuraes Avanadas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHOW_HIDDEN_FILES, "Exibir Arquivos e Pastas Ocultos" @@ -1689,7 +1735,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SHUTDOWN, "Desligar" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SLOWMOTION_RATIO, - "Taxa de Câmera Lenta" + "Taxa de Cmera Lenta" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVEFILES_ENABLE, "Classificar Arquivos de Jogo-Salvo em Pastas" @@ -1698,52 +1744,52 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVESTATES_ENABLE, "Classificar Arquivos de Estado de Jogo em Pastas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATES_IN_CONTENT_DIR_ENABLE, - "Gravar Estados de Jogo no Diretório de Conteúdo" + "Gravar Estados de Jogo no Diretrio de Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVEFILES_IN_CONTENT_DIR_ENABLE, - "Gravar Jogos-Salvos no Diretório de Conteúdo" + "Gravar Jogos-Salvos no Diretrio de Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEMFILES_IN_CONTENT_DIR_ENABLE, - "Arquivos de Sistema estão no Diretório de Conteúdo" + "Arquivos de Sistema esto no Diretrio de Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCREENSHOTS_IN_CONTENT_DIR_ENABLE, - "Salvar Capturas de Tela no Diretório de Conteúdo" + "Salvar Capturas de Tela no Diretrio de Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SSH_ENABLE, "Habilitar SSH" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_START_CORE, - "Iniciar Núcleo" + "Iniciar Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_START_NET_RETROPAD, "Iniciar RetroPad Remoto" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_START_VIDEO_PROCESSOR, - "Iniciar Processador de Vídeo" + "Iniciar Processador de Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_STATE_SLOT, "Compartimento do Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_STATUS, - "Condição" + "Condio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_STDIN_CMD_ENABLE, "Comandos stdin" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SUPPORTED_CORES, - "Núcleos Sugeridos" + "Ncleos Sugeridos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SUSPEND_SCREENSAVER_ENABLE, "Desativar Protetor de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_BGM_ENABLE, - "Habilitar Música em Segundo Plano do Sistema" + "Habilitar Msica em Segundo Plano do Sistema" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_DIRECTORY, "Sistema/BIOS" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFORMATION, - "Informação do Sistema" + "Informao do Sistema" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_7ZIP_SUPPORT, "Suporte a 7zip" @@ -1752,7 +1798,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_ALSA_SUPPORT, "Suporte a ALSA" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_BUILD_DATE, - "Data de Compilação" + "Data de Compilao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_CG_SUPPORT, "Suporte a Cg" @@ -1761,22 +1807,22 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COCOA_SUPPORT, "Suporte a Cocoa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COMMAND_IFACE_SUPPORT, - "Suporte à Interface de Comando" + "Suporte Interface de Comando" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_CORETEXT_SUPPORT, "Suporte a CoreText" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_CPU_FEATURES, - "Características de CPU" + "Caractersticas de CPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_DPI, - "Métrica DPI da Tela" + "Mtrica DPI da Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_HEIGHT, - "Métrica de Altura da Tela (mm)" + "Mtrica de Altura da Tela (mm)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, - "Métrica de Largura da Tela (mm)" + "Mtrica de Largura da Tela (mm)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "Suporte a DirectSound" @@ -1785,10 +1831,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, "Suporte a WASAPI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, - "Suporte à biblioteca dinâmica" + "Suporte biblioteca dinmica" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, - "Carregamento dinâmico em tempo de execução da biblioteca libretro" + "Carregamento dinmico em tempo de execuo da biblioteca libretro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_EGL_SUPPORT, "Suporte a EGL" @@ -1812,7 +1858,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_FRONTEND_OS, "SO do Frontend" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_GIT_VERSION, - "Versão Git" + "Verso Git" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_GLSL_SUPPORT, "Suporte a GLSL" @@ -1827,7 +1873,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_KMS_SUPPORT, "Suporte a KMS/EGL" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LAKKA_VERSION, - "Versão Lakka" + "Verso Lakka" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LIBRETRODB_SUPPORT, "Suporte a LibretroDB" @@ -1842,7 +1888,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_NETPLAY_SUPPORT, "Suporte Netplay (ponto-a-ponto)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_NETWORK_COMMAND_IFACE_SUPPORT, - "Suporte à Interface de comando de rede" + "Suporte Interface de comando de rede" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_NETWORK_REMOTE_SUPPORT, "Suporte a Gamepad de Rede" @@ -1866,7 +1912,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OSS_SUPPORT, "Suporte a OSS" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OVERLAY_SUPPORT, - "Suporte à Transparência" + "Suporte Transparncia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_POWER_SOURCE, "Fonte de Energia" @@ -1881,7 +1927,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_POWER_SOURCE_DISCHARGING, "Descarregando" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_POWER_SOURCE_NO_SOURCE, - "Não há fonte" + "No h fonte" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_PULSEAUDIO_SUPPORT, "Suporte a PulseAudio" @@ -1893,7 +1939,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RBMP_SUPPORT, "Suporte a BMP (RBMP)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RETRORATING_LEVEL, - "Nível RetroRating" + "Nvel RetroRating" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RJPEG_SUPPORT, "Suporte a JPEG (RJPEG)" @@ -1932,7 +1978,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_V4L2_SUPPORT, "Suporte a Video4Linux2" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_VIDEO_CONTEXT_DRIVER, - "Driver de contexto de vídeo" + "Driver de contexto de vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_VULKAN_SUPPORT, "Suporte a Vulkan" @@ -1959,8 +2005,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_THREADED_DATA_RUNLOOP_ENABLE, "Paralelismo de tarefas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS, - "Miniaturas" - ) + "Miniaturas") +MSG_HASH(MENU_ENUM_LABEL_VALUE_LEFT_THUMBNAILS, + "Miniaturas esquerda") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS_DIRECTORY, "Miniaturas" ) @@ -1974,22 +2021,22 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_SCREENSHOTS, "Captura de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_TITLE_SCREENS, - "Tela do Título" + "Tela do Ttulo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_TIMEDATE_ENABLE, "Exibir data / hora" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_TITLE_COLOR, - "Cor do título do menu" + "Cor do ttulo do menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_TRUE, "Verdadeiro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_ENABLE, - "Habilitar Companheiro da Interface de Usuário" + "Habilitar Companheiro da Interface de Usurio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_START_ON_BOOT, - "Companheiro da Interface de Usuário Roda na Inicialização" + "Companheiro da Interface de Usurio Roda na Inicializao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_MENUBAR_ENABLE, "Barra de Menu" @@ -2013,16 +2060,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_ASSETS, "Atualizar Recursos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_AUTOCONFIG_PROFILES, - "Atualizar Perfis de Autoconfiguração" + "Atualizar Perfis de Autoconfigurao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_CG_SHADERS, "Atualizar Shaders Cg" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_CHEATS, - "Atualizar Trapaças" + "Atualizar Trapaas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_CORE_INFO_FILES, - "Atualizar Arquivos de Informação de Núcleo" + "Atualizar Arquivos de Informao de Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_DATABASES, "Atualizar Bases de Dados" @@ -2034,79 +2081,79 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_LAKKA, "Atualizar Lakka" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_OVERLAYS, - "Atualizar Transparências" + "Atualizar Transparncias" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_SLANG_SHADERS, "Atualizar Shaders Slang" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USER, - "Usuário" + "Usurio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USER_INTERFACE_SETTINGS, - "Interface de Usuário" + "Interface de Usurio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USER_LANGUAGE, "Idioma" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USER_SETTINGS, - "Usuário" + "Usurio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USE_BUILTIN_IMAGE_VIEWER, "Utilizar o Visualizador de Imagem Integrado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USE_BUILTIN_PLAYER, - "Utilizar o Reprodutor de Mídia Integrado" + "Utilizar o Reprodutor de Mdia Integrado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USE_THIS_DIRECTORY, - "" + "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ALLOW_ROTATE, - "Permitir rotação" + "Permitir rotao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO, - "Configurar Proporção de Tela" + "Configurar Proporo de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_AUTO, - "Proporção de Tela Automática" + "Proporo de Tela Automtica" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_INDEX, - "Proporção de Tela" + "Proporo de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_BLACK_FRAME_INSERTION, - "Inserção de Quadro Opaco" + "Insero de Quadro Opaco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_CROP_OVERSCAN, "Cortar Overscan (Recarregar)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_DISABLE_COMPOSITION, - "Desativar Composição da Área de Trabalho" + "Desativar Composio da rea de Trabalho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_DRIVER, - "Driver de Vídeo" + "Driver de Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FILTER, - "Filtro de Vídeo" + "Filtro de Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FILTER_DIR, - "Filtro de Vídeo" + "Filtro de Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FILTER_FLICKER, - "Filtro de tremulação de vídeo" + "Filtro de tremulao de vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FONT_ENABLE, - "Habilitar Notificações na Tela" + "Habilitar Notificaes na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FONT_PATH, - "Fonte das Notificações na Tela" + "Fonte das Notificaes na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FONT_SIZE, - "Tamanho da Notificação na Tela" + "Tamanho da Notificao na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_ASPECT, - "Forçar Proporção de Tela" + "Forar Proporo de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_SRGB_DISABLE, - "Forçar Desativação de sRGB FBO" + "Forar Desativao de sRGB FBO" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_DELAY, "Atraso de Quadro" @@ -2115,43 +2162,43 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FULLSCREEN, "Utilizar Modo de Tela Cheia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_GAMMA, - "Gama de Vídeo" + "Gama de Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_GPU_RECORD, - "Usar Gravação da GPU" + "Usar Gravao da GPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_GPU_SCREENSHOT, "Habilitar Captura de Tela da GPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC, - "Sincronia Rígida de GPU" + "Sincronia Rgida de GPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC_FRAMES, - "Quadros de Sincronia Rígida de GPU" + "Quadros de Sincronia Rgida de GPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MAX_SWAPCHAIN_IMAGES, - "Máximo de imagens na cadeia de troca" + "Mximo de imagens na cadeia de troca" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_POS_X, - "Posição X da Notificação na Tela" + "Posio X da Notificao na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_POS_Y, - "Posição Y da Notificação na Tela" + "Posio Y da Notificao na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MONITOR_INDEX, - "Índice de Monitor" + "ndice de Monitor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_POST_FILTER_RECORD, - "Usar Gravação Pós-Filtro" + "Usar Gravao Ps-Filtro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, - "Taxa de Atualização Vertical" + "Taxa de Atualizao Vertical" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Taxa de Quadros Estimada da Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, - "Rotação" + "Rotao" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, "Escala em Janela" @@ -2160,28 +2207,28 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE_INTEGER, "Escala em Inteiros" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SETTINGS, - "Vídeo" + "Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_DIR, - "Shader de Vídeo" + "Shader de Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_NUM_PASSES, - "Estágios de Shader" + "Estgios de Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PARAMETERS, - "Pré-visualizar Parâmetros de Shader" + "Pr-visualizar Parmetros de Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET, - "Carregar Predefinição de Shader" + "Carregar Predefinio de Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS, - "Salvar Predefinição de Shader Como" + "Salvar Predefinio de Shader Como" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_CORE, - "Salvar Predefinição de Núcleo" + "Salvar Predefinio de Ncleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_GAME, - "Salvar Predefinição de Jogo" + "Salvar Predefinio de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHARED_CONTEXT, "Habilitar Contexto Compartilhado de Hardware" @@ -2193,34 +2240,34 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SOFT_FILTER, "Habilitar Filtro por Software" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SWAP_INTERVAL, - "Intervalo de Troca da Sincronização Vertical (V-Sync)" + "Intervalo de Troca da Sincronizao Vertical (V-Sync)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_TAB, - "Vídeo" + "Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_THREADED, - "Vídeo Paralelizado" + "Vdeo Paralelizado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VFILTER, - "Reduzir Tremulação de Vídeo" + "Reduzir Tremulao de Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_HEIGHT, - "Altura Personalizada da Proporção de Tela" + "Altura Personalizada da Proporo de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_WIDTH, - "Largura Personalizada da Proporção de Tela" + "Largura Personalizada da Proporo de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_X, - "Posição X Personalizada da Proporção de Tela" + "Posio X Personalizada da Proporo de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_Y, - "Posição Y Personalizada da Proporção de Tela" + "Posio Y Personalizada da Proporo de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VI_WIDTH, "Definir Largura de Tela do VI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VSYNC, - "Sincronização Vertical (V-Sync)" + "Sincronizao Vertical (V-Sync)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOWED_FULLSCREEN, "Modo Janela em Tela Cheia" @@ -2256,13 +2303,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_FLATUI, "FlatUI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_MONOCHROME, - "Monocromático" + "Monocromtico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_MONOCHROME_INVERTED, - "Monocromático Inverted" + "Monocromtico Inverted" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_SYSTEMATIC, - "Sistemático" + "Sistemtico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_NEOACTIVE, "NeoActive" @@ -2283,7 +2330,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME, "Tema de Cor do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_APPLE_GREEN, - "Verde Maçã" + "Verde Ma" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_DARK, "Escuro" @@ -2292,7 +2339,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_DARK_PURPLE, "Roxo Escuro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_ELECTRIC_BLUE, - "Azul Elétrico" + "Azul Eltrico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_GOLDEN, "Dourado" @@ -2310,7 +2357,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_UNDERSEA, "Submarino" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_VOLCANIC_RED, - "Vermelho Vulcânico" + "Vermelho Vulcnico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_RIBBON_ENABLE, "Pipeline do Shader de Menu" @@ -2319,13 +2366,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SCALE_FACTOR, "Fator de Escala do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SHADOWS_ENABLE, - "Habilitar Sombras dos Ícones" + "Habilitar Sombras dos cones" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_HISTORY, - "Exibir Aba de Histórico" + "Exibir Aba de Histrico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_ADD, - "Exibir Aba de Importação de Conteúdo" + "Exibir Aba de Importao de Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_FAVORITES, "Exibir Aba de Favoritos" @@ -2334,101 +2381,101 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_IMAGES, "Exibir Aba de Imagem" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_MUSIC, - "Exibir Aba de Música" + "Exibir Aba de Msica" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS, - "Exibir Aba de Configurações" + "Exibir Aba de Configuraes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, - "Exibir Aba de Vídeo" + "Exibir Aba de Vdeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Exibir Aba de Netplay" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, - "Tema de Ícones do Menu" + "Tema de cones do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, "Sim" ) MSG_HASH(MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_TWO, - "Predefinição de Shader" + "Predefinio de Shader" ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_ENABLE, - "Habilitar ou desabilitar conquistas. Para mais informações, visite http://retroachievements.org" + "Habilitar ou desabilitar conquistas. Para mais informaes, visite http://retroachievements.org" ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, - "Habilitar ou desabilitar conquistas não oficiais e/ou recursos beta para fins de teste." + "Habilitar ou desabilitar conquistas no oficiais e/ou recursos beta para fins de teste." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, - "Habilitar ou desabilitar Estado de Jogo, Trapaças, Voltar Atrás, Avanço Rápido, Pausa e Câmera Lenta para todos os jogos.") + "Habilitar ou desabilitar Estado de Jogo, Trapaas, Voltar Atrs, Avano Rpido, Pausa e Cmera Lenta para todos os jogos.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_LEADERBOARDS_ENABLE, - "Ativar ou desativar tabelas de classificação no jogo. Não tem efeito se o modo Hardcore estiver desativado.") + "Ativar ou desativar tabelas de classificao no jogo. No tem efeito se o modo Hardcore estiver desativado.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_BADGES_ENABLE, - "Ativar ou desativar a exibição de insígnia na Lista de Conquistas.") + "Ativar ou desativar a exibio de insgnia na Lista de Conquistas.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_VERBOSE_ENABLE, "Habilitar ou desabilitar detalhes das conquistas na tela.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_AUTO_SCREENSHOT, - "Obter automaticamente uma captura de tela quando uma conquista é acionada.") + "Obter automaticamente uma captura de tela quando uma conquista acionada.") MSG_HASH(MENU_ENUM_SUBLABEL_DRIVER_SETTINGS, "Alterar os drivers utilizados pelo sistema." ) MSG_HASH(MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS, - "Alterar as configurações de conquistas." + "Alterar as configuraes de conquistas." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_SETTINGS, - "Alterar as configurações de núcleo." + "Alterar as configuraes de ncleo." ) MSG_HASH(MENU_ENUM_SUBLABEL_RECORDING_SETTINGS, - "Alterar as configurações de gravação." + "Alterar as configuraes de gravao." ) MSG_HASH(MENU_ENUM_SUBLABEL_ONSCREEN_DISPLAY_SETTINGS, - "Alterar as configurações de Transparência e Transparência de teclado, e as configurações de notificação na tela." + "Alterar as configuraes de Transparncia e Transparncia de teclado, e as configuraes de notificao na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_FRAME_THROTTLE_SETTINGS, - "Alterar as configurações de Voltar Atrás, Avanço Rápido e Câmera Lenta." + "Alterar as configuraes de Voltar Atrs, Avano Rpido e Cmera Lenta." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVING_SETTINGS, - "Alterar as configurações de salvamento." + "Alterar as configuraes de salvamento." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOGGING_SETTINGS, - "Alterar as configurações de registro de eventos." + "Alterar as configuraes de registro de eventos." ) MSG_HASH(MENU_ENUM_SUBLABEL_USER_INTERFACE_SETTINGS, - "Alterar as configurações da interface de usuário." + "Alterar as configuraes da interface de usurio." ) MSG_HASH(MENU_ENUM_SUBLABEL_USER_SETTINGS, - "Alterar as configurações de conta, nome de usuário e idioma." + "Alterar as configuraes de conta, nome de usurio e idioma." ) MSG_HASH(MENU_ENUM_SUBLABEL_PRIVACY_SETTINGS, - "Alterar as configurações de privacidade." + "Alterar as configuraes de privacidade." ) MSG_HASH(MENU_ENUM_SUBLABEL_DIRECTORY_SETTINGS, - "Alterar os diretórios padrão onde os arquivos estão localizados." + "Alterar os diretrios padro onde os arquivos esto localizados." ) MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_SETTINGS, - "Alterar as configurações de lista de reprodução." + "Alterar as configuraes de lista de reproduo." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETWORK_SETTINGS, - "Configurar as configurações de servidor e rede." + "Configurar as configuraes de servidor e rede." ) MSG_HASH(MENU_ENUM_SUBLABEL_ADD_CONTENT_LIST, - "Analisar conteúdo e adicionar na base de dados." + "Analisar contedo e adicionar na base de dados." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_SETTINGS, - "Alterar as configurações de saída de áudio." + "Alterar as configuraes de sada de udio." ) MSG_HASH(MENU_ENUM_SUBLABEL_BLUETOOTH_ENABLE, "Habilitar ou desabilitar o bluetooth." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONFIG_SAVE_ON_EXIT, - "Salvar as alterações nos arquivos de configuração ao sair." + "Salvar as alteraes nos arquivos de configurao ao sair." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONFIGURATION_SETTINGS, - "Alterar as definições padrão para os arquivos de configuração." + "Alterar as definies padro para os arquivos de configurao." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONFIGURATIONS_LIST, - "Gerenciar e criar arquivos de configuração." + "Gerenciar e criar arquivos de configurao." ) MSG_HASH(MENU_ENUM_SUBLABEL_CPU_CORES, "Quantidade de Cores que a CPU possui." @@ -2437,76 +2484,76 @@ MSG_HASH(MENU_ENUM_SUBLABEL_FPS_SHOW, "Exibir a taxa atual de quadros por segundo na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_HOTKEY_BINDS, - "Ajustar configurações das teclas de atalho." + "Ajustar configuraes das teclas de atalho." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_MENU_ENUM_TOGGLE_GAMEPAD_COMBO, - "Combinação de botões do Gamepad para alternar o menu." + "Combinao de botes do Gamepad para alternar o menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_SETTINGS, - "Alterar as configurações de Joypad, teclado e Mouse." + "Alterar as configuraes de Joypad, teclado e Mouse." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_USER_BINDS, - "Configurar os controles para este usuário." + "Configurar os controles para este usurio." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOG_VERBOSITY, "Habilitar ou desabilitar registro de eventos no terminal." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY, - "Juntar-se ou hospedar uma sessão de Netplay." + "Juntar-se ou hospedar uma sesso de Netplay." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_LAN_SCAN_SETTINGS, "Procurar por e conectar aos hospedeiros de Netplay na rede local." ) MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST, - "Exibir informações de núcleo, rede e sistema." + "Exibir informaes de ncleo, rede e sistema." ) MSG_HASH(MENU_ENUM_SUBLABEL_ONLINE_UPDATER, - "Baixar complementos, componentes e conteúdo para o RetroArch." + "Baixar complementos, componentes e contedo para o RetroArch." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAMBA_ENABLE, "Habilitar ou desabilitar compartilhamento de pastas na rede." ) MSG_HASH(MENU_ENUM_SUBLABEL_SERVICES_SETTINGS, - "Gerenciar serviços ao nível de sistema operacional." + "Gerenciar servios ao nvel de sistema operacional." ) MSG_HASH(MENU_ENUM_SUBLABEL_SHOW_HIDDEN_FILES, - "Exibir arquivos/diretórios ocultos no navegador de arquivos." + "Exibir arquivos/diretrios ocultos no navegador de arquivos." ) MSG_HASH(MENU_ENUM_SUBLABEL_SSH_ENABLE, - "Habilitar ou desabilitar acesso remoto à linha de comando." + "Habilitar ou desabilitar acesso remoto linha de comando." ) MSG_HASH(MENU_ENUM_SUBLABEL_SUSPEND_SCREENSAVER_ENABLE, "Prevenir que o protetor de tela do seu sistema seja ativado." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_WINDOW_SCALE, - "Definir o tamanho da janela em relação ao tamanho da janela de exibição do núcleo. Como alternativa, você pode definir uma largura e altura de janela abaixo para um tamanho de janela fixo." + "Definir o tamanho da janela em relao ao tamanho da janela de exibio do ncleo. Como alternativa, voc pode definir uma largura e altura de janela abaixo para um tamanho de janela fixo." ) MSG_HASH(MENU_ENUM_SUBLABEL_USER_LANGUAGE, "Definir o idioma da interface." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_BLACK_FRAME_INSERTION, - "Inserir um quadro opaco entre quadros. Útil para usuários com telas de 120Hz que desejam jogar conteúdos em 60Hz para eliminar efeito de fantasma." + "Inserir um quadro opaco entre quadros. til para usurios com telas de 120Hz que desejam jogar contedos em 60Hz para eliminar efeito de fantasma." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY, - "Reduz a latência ao custo de maior risco de engasgamento de vídeo. Adiciona um atraso após o V-Sync (em ms)." + "Reduz a latncia ao custo de maior risco de engasgamento de vdeo. Adiciona um atraso aps o V-Sync (em ms)." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC_FRAMES, - "Definir quantos quadros a CPU pode rodar à frente da GPU quando utilizado o recurso 'Sincronia Rígida de GPU'." + "Definir quantos quadros a CPU pode rodar frente da GPU quando utilizado o recurso 'Sincronia Rgida de GPU'." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MAX_SWAPCHAIN_IMAGES, - "Informar ao driver de vídeo para utilizar explicitamente um modo de buffer específico." + "Informar ao driver de vdeo para utilizar explicitamente um modo de buffer especfico." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, - "Seleciona qual tela de exibição a ser usada." + "Seleciona qual tela de exibio a ser usada." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, - "A taxa de atualização estimada da tela em Hz." + "A taxa de atualizao estimada da tela em Hz." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, - "Alterar as configurações de saída de vídeo." + "Alterar as configuraes de sada de vdeo." ) MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, - "Analisar por redes sem fio e estabelecer uma conexão." + "Analisar por redes sem fio e estabelecer uma conexo." ) MSG_HASH(MENU_ENUM_SUBLABEL_HELP_LIST, "Saiba mais sobre como o programa funciona." @@ -2518,28 +2565,28 @@ MSG_HASH(MSG_APPENDED_DISK, "Disco anexado" ) MSG_HASH(MSG_APPLICATION_DIR, - "Diretório do aplicativo" + "Diretrio do aplicativo" ) MSG_HASH(MSG_APPLYING_CHEAT, - "Aplicando as alterações de Trapaças." + "Aplicando as alteraes de Trapaas." ) MSG_HASH(MSG_APPLYING_SHADER, "Aplicando Shader" ) MSG_HASH(MSG_AUDIO_MUTED, - "Áudio mudo." + "udio mudo." ) MSG_HASH(MSG_AUDIO_UNMUTED, - "Áudio mudo desativado." + "udio mudo desativado." ) MSG_HASH(MSG_AUTOCONFIG_FILE_ERROR_SAVING, - "Erro em salvar o arquivo de autoconfiguração." + "Erro em salvar o arquivo de autoconfigurao." ) MSG_HASH(MSG_AUTOCONFIG_FILE_SAVED_SUCCESSFULLY, - "Arquivo de autoconfiguração salvo com sucesso." + "Arquivo de autoconfigurao salvo com sucesso." ) MSG_HASH(MSG_AUTOSAVE_FAILED, - "Não foi possível inicializar o autossalvamento." + "No foi possvel inicializar o autossalvamento." ) MSG_HASH(MSG_AUTO_SAVE_STATE_TO, "Autosalvar Estado de Jogo em" @@ -2554,76 +2601,76 @@ MSG_HASH(MSG_BYTES, "bytes" ) MSG_HASH(MSG_CANNOT_INFER_NEW_CONFIG_PATH, - "Não é possível inferir o novo caminho de configuração. Use a hora atual." + "No possvel inferir o novo caminho de configurao. Use a hora atual." ) MSG_HASH(MSG_CHEEVOS_HARDCORE_MODE_ENABLE, - "Modo Hardcore habilitado, Estados de Jogo e Voltar Atrás estão desabilitados." + "Modo Hardcore habilitado, Estados de Jogo e Voltar Atrs esto desabilitados." ) MSG_HASH(MSG_COMPARING_WITH_KNOWN_MAGIC_NUMBERS, - "Comparando com números mágicos conhecidos..." + "Comparando com nmeros mgicos conhecidos..." ) MSG_HASH(MSG_COMPILED_AGAINST_API, "Compilado contra a API" ) MSG_HASH(MSG_CONFIG_DIRECTORY_NOT_SET, - "Diretório de configuração não definido. Não foi possível salvar a nova configuração." + "Diretrio de configurao no definido. No foi possvel salvar a nova configurao." ) MSG_HASH(MSG_CONNECTED_TO, "Conectado a" ) MSG_HASH(MSG_CONTENT_CRC32S_DIFFER, - "O CRC32 dos conteúdos difere. Não é possível utilizar jogos diferentes." + "O CRC32 dos contedos difere. No possvel utilizar jogos diferentes." ) MSG_HASH(MSG_CONTENT_LOADING_SKIPPED_IMPLEMENTATION_WILL_DO_IT, - "Carregamento de conteúdo ignorado. A implementação irá carregar por conta própria." + "Carregamento de contedo ignorado. A implementao ir carregar por conta prpria." ) MSG_HASH(MSG_CORE_DOES_NOT_SUPPORT_SAVESTATES, - "O núcleo não suporta Estados de Jogo." + "O ncleo no suporta Estados de Jogo." ) MSG_HASH(MSG_CORE_OPTIONS_FILE_CREATED_SUCCESSFULLY, - "O arquivo de opções de núcleo foi criado com sucesso." + "O arquivo de opes de ncleo foi criado com sucesso." ) MSG_HASH(MSG_COULD_NOT_FIND_ANY_NEXT_DRIVER, - "Não foi possível encontrar nenhum driver seguinte" + "No foi possvel encontrar nenhum driver seguinte" ) MSG_HASH(MSG_COULD_NOT_FIND_COMPATIBLE_SYSTEM, - "Não foi possível encontrar um sistema compatível." + "No foi possvel encontrar um sistema compatvel." ) MSG_HASH(MSG_COULD_NOT_FIND_VALID_DATA_TRACK, - "Não foi possível encontrar uma faixa de dados válida" + "No foi possvel encontrar uma faixa de dados vlida" ) MSG_HASH(MSG_COULD_NOT_OPEN_DATA_TRACK, - "Não foi possível abrir a faixa de dados" + "No foi possvel abrir a faixa de dados" ) MSG_HASH(MSG_COULD_NOT_READ_CONTENT_FILE, - "Não foi possível ler o arquivo de conteúdo" + "No foi possvel ler o arquivo de contedo" ) MSG_HASH(MSG_COULD_NOT_READ_MOVIE_HEADER, - "Não foi possível ler o cabeçalho do filme." + "No foi possvel ler o cabealho do filme." ) MSG_HASH(MSG_COULD_NOT_READ_STATE_FROM_MOVIE, - "Não foi possível ler o Estado de Jogo do filme." + "No foi possvel ler o Estado de Jogo do filme." ) MSG_HASH(MSG_CRC32_CHECKSUM_MISMATCH, - "Soma de verificação CRC32 incompatível entre o arquivo de conteúdo e a soma de verificação de conteúdo salva no cabeçalho do arquivo de reprodução. Reprodução altamente susceptível de dessincronizar na reprodução." + "Soma de verificao CRC32 incompatvel entre o arquivo de contedo e a soma de verificao de contedo salva no cabealho do arquivo de reproduo. Reproduo altamente susceptvel de dessincronizar na reproduo." ) MSG_HASH(MSG_CUSTOM_TIMING_GIVEN, "Tempo personalizado fornecido" ) MSG_HASH(MSG_DECOMPRESSION_ALREADY_IN_PROGRESS, - "Descompressão já está em andamento." + "Descompresso j est em andamento." ) MSG_HASH(MSG_DECOMPRESSION_FAILED, - "Descompressão falhou." + "Descompresso falhou." ) MSG_HASH(MSG_DETECTED_VIEWPORT_OF, - "Detectada janela de exibição de" + "Detectada janela de exibio de" ) MSG_HASH(MSG_DID_NOT_FIND_A_VALID_CONTENT_PATCH, - "Não encontrou uma modificação de conteúdo válido." + "No encontrou uma modificao de contedo vlido." ) MSG_HASH(MSG_DISCONNECT_DEVICE_FROM_A_VALID_PORT, - "Desconectar dispositivo de uma porta válida." + "Desconectar dispositivo de uma porta vlida." ) MSG_HASH(MSG_DISK_CLOSED, "Fechado" @@ -2641,16 +2688,16 @@ MSG_HASH(MSG_ERROR, "Erro" ) MSG_HASH(MSG_ERROR_LIBRETRO_CORE_REQUIRES_CONTENT, - "O núcleo libretro requer conteúdo, mas nada foi fornecido." + "O ncleo libretro requer contedo, mas nada foi fornecido." ) MSG_HASH(MSG_ERROR_LIBRETRO_CORE_REQUIRES_SPECIAL_CONTENT, - "O núcleo libretro requer conteúdo especial, mas nenhum foi fornecido." + "O ncleo libretro requer contedo especial, mas nenhum foi fornecido." ) MSG_HASH(MSG_ERROR_PARSING_ARGUMENTS, "Erro em analisar os argumentos." ) MSG_HASH(MSG_ERROR_SAVING_CORE_OPTIONS_FILE, - "Erro em salvar o arquivo de opções de núcleo." + "Erro em salvar o arquivo de opes de ncleo." ) MSG_HASH(MSG_ERROR_SAVING_REMAP_FILE, "Erro em salvar o arquivo de remapeamento." @@ -2659,10 +2706,10 @@ MSG_HASH(MSG_ERROR_REMOVING_REMAP_FILE, "Erro em remover o arquivo de remapeamento." ) MSG_HASH(MSG_ERROR_SAVING_SHADER_PRESET, - "Erro em salvar a predefinição de Shader." + "Erro em salvar a predefinio de Shader." ) MSG_HASH(MSG_EXTERNAL_APPLICATION_DIR, - "Diretório de Aplicativo Externo" + "Diretrio de Aplicativo Externo" ) MSG_HASH(MSG_EXTRACTING, "Extraindo" @@ -2671,7 +2718,7 @@ MSG_HASH(MSG_EXTRACTING_FILE, "Extraindo arquivo" ) MSG_HASH(MSG_FAILED_SAVING_CONFIG_TO, - "Falha em salvar a configuração em" + "Falha em salvar a configurao em" ) MSG_HASH(MSG_FAILED_TO, "Falha em" @@ -2680,7 +2727,7 @@ MSG_HASH(MSG_FAILED_TO_ACCEPT_INCOMING_SPECTATOR, "Falha em aceitar o espectador ingresso." ) MSG_HASH(MSG_FAILED_TO_ALLOCATE_MEMORY_FOR_PATCHED_CONTENT, - "Falha em alocar memória para o conteúdo modificado..." + "Falha em alocar memria para o contedo modificado..." ) MSG_HASH(MSG_FAILED_TO_APPLY_SHADER, "Falha em aplicar o Shader." @@ -2689,10 +2736,10 @@ MSG_HASH(MSG_FAILED_TO_BIND_SOCKET, "Falha em vincular o soquete." ) MSG_HASH(MSG_FAILED_TO_CREATE_THE_DIRECTORY, - "Falha em criar o diretório." + "Falha em criar o diretrio." ) MSG_HASH(MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE, - "Falha em extrair o conteúdo do arquivo comprimido" + "Falha em extrair o contedo do arquivo comprimido" ) MSG_HASH(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT, "Falha em obter o apelido do cliente." @@ -2701,25 +2748,25 @@ MSG_HASH(MSG_FAILED_TO_LOAD, "Falha em carregar" ) MSG_HASH(MSG_FAILED_TO_LOAD_CONTENT, - "Falha em carregar o conteúdo" + "Falha em carregar o contedo" ) MSG_HASH(MSG_FAILED_TO_LOAD_MOVIE_FILE, "Falha em carregar o arquivo de filme" ) MSG_HASH(MSG_FAILED_TO_LOAD_OVERLAY, - "Falha em carregar a Transparência." + "Falha em carregar a Transparncia." ) MSG_HASH(MSG_FAILED_TO_LOAD_STATE, "Falha em carregar o Estado de Jogo de" ) MSG_HASH(MSG_FAILED_TO_OPEN_LIBRETRO_CORE, - "Falha em abrir o núcleo Libretro" + "Falha em abrir o ncleo Libretro" ) MSG_HASH(MSG_FAILED_TO_PATCH, - "Falha em executar a modificação" + "Falha em executar a modificao" ) MSG_HASH(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT, - "Falha em receber o cabeçalho do cliente." + "Falha em receber o cabealho do cliente." ) MSG_HASH(MSG_FAILED_TO_RECEIVE_NICKNAME, "Falha em receber o apelido." @@ -2737,7 +2784,7 @@ MSG_HASH(MSG_FAILED_TO_REMOVE_DISK_FROM_TRAY, "Falha em remover o disco da bandeja." ) MSG_HASH(MSG_FAILED_TO_REMOVE_TEMPORARY_FILE, - "Falha em remover o arquivo temporário" + "Falha em remover o arquivo temporrio" ) MSG_HASH(MSG_FAILED_TO_SAVE_SRAM, "Falha em salvar SRAM" @@ -2761,13 +2808,13 @@ MSG_HASH(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT, "Falha em enviar os dados SRAM para o cliente." ) MSG_HASH(MSG_FAILED_TO_START_AUDIO_DRIVER, - "Falha em iniciar o driver de áudio. Prosseguindo sem áudio." + "Falha em iniciar o driver de udio. Prosseguindo sem udio." ) MSG_HASH(MSG_FAILED_TO_START_MOVIE_RECORD, - "Falha em iniciar a gravação do filme." + "Falha em iniciar a gravao do filme." ) MSG_HASH(MSG_FAILED_TO_START_RECORDING, - "Falha em iniciar a gravação." + "Falha em iniciar a gravao." ) MSG_HASH(MSG_FAILED_TO_TAKE_SCREENSHOT, "Falha em obter uma captura de tela." @@ -2778,26 +2825,66 @@ MSG_HASH(MSG_FAILED_TO_UNDO_LOAD_STATE, MSG_HASH(MSG_FAILED_TO_UNDO_SAVE_STATE, "Falha em desfazer o salvamento de Estado de Jogo." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_SAVESTATE_THUMBNAIL_ENABLE, + "Mostrar miniaturas de estados salvos dentro do menu." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_AUTOSAVE_INTERVAL, + "Salvar automaticamente o Save RAM no-voltil em um intervalo regular. Isso est desabilitado por padro, a menos que seja definido de outra forma. O intervalo medido em segundos. Um valor de 0 desativa o salvamento automtico." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE, + "Se ativado, substitui os vnculos de entrada com as associaes remapeadas definidas para o ncleo atual." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_AUTODETECT_ENABLE, + "Ativa a deteco automtica de entrada. Tentar autoconfigurar joypads, estilo Plug-and-Play." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MENU_INPUT_SWAP_OK_CANCEL, + "Troca de botes para OK/Cancelar. Desabilitado o estilo de boto japons, habilitada oestilo ocidental." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_PAUSE_LIBRETRO, + "Se desabilitado, o contedo continuar sendo executado em segundo plano quando o menu do RetroArch for alternado." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_DRIVER, + "Driver de vdeo para usar." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_AUDIO_DRIVER, + "Driver de udio para usar." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_DRIVER, + "Driver de entrada para usar. Dependendo do driver de vdeo, pode forar um driver de entrada diferente." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_JOYPAD_DRIVER, + "Driver do Joypad para usar." + ) MSG_HASH(MSG_FAILED_TO_UNMUTE_AUDIO, - "Falha em desativar o áudio mudo." + "Falha em desativar o udio mudo." ) MSG_HASH(MSG_FATAL_ERROR_RECEIVED_IN, "Erro fatal recebido em" ) MSG_HASH(MSG_FILE_NOT_FOUND, - "Arquivo não encontrado" + "Arquivo no encontrado" ) MSG_HASH(MSG_FOUND_AUTO_SAVESTATE_IN, - "Estado de Jogo automático encontrado em" + "Estado de Jogo automtico encontrado em" ) MSG_HASH(MSG_FOUND_DISK_LABEL, - "Rótulo de disco encontrado" + "Rtulo de disco encontrado" ) MSG_HASH(MSG_FOUND_FIRST_DATA_TRACK_ON_FILE, "Encontrada primeira faixa de dados no arquivo" ) MSG_HASH(MSG_FOUND_LAST_STATE_SLOT, - "Encontrada último compartimento de Estado de Jogo" + "Encontrada ltimo compartimento de Estado de Jogo" ) MSG_HASH(MSG_FOUND_SHADER, "Shader encontrado" @@ -2806,10 +2893,10 @@ MSG_HASH(MSG_FRAMES, "Quadros" ) MSG_HASH(MSG_GAME_SPECIFIC_CORE_OPTIONS_FOUND_AT, - "Opções por Jogo: Opções de núcleo específicas do jogo encontradas em" + "Opes por Jogo: Opes de ncleo especficas do jogo encontradas em" ) MSG_HASH(MSG_GOT_INVALID_DISK_INDEX, - "Índice de disco inválido obtido" + "ndice de disco invlido obtido" ) MSG_HASH(MSG_GRAB_MOUSE_STATE, "Capturar estado do Mouse" @@ -2821,22 +2908,22 @@ MSG_HASH(MSG_GAME_FOCUS_OFF, "Foco do jogo desligado" ) MSG_HASH(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING, - "O núcleo libretro é renderizado por hardware. Deve usar a gravação pós-Shader também." + "O ncleo libretro renderizado por hardware. Deve usar a gravao ps-Shader tambm." ) MSG_HASH(MSG_INFLATED_CHECKSUM_DID_NOT_MATCH_CRC32, - "A soma de verificação inflada não corresponde ao CRC32." + "A soma de verificao inflada no corresponde ao CRC32." ) MSG_HASH(MSG_INPUT_CHEAT, - "Entrada de Trapaça" + "Entrada de Trapaa" ) MSG_HASH(MSG_INPUT_CHEAT_FILENAME, - "Nome do Arquivo de Trapaça" + "Nome do Arquivo de Trapaa" ) MSG_HASH(MSG_INPUT_PRESET_FILENAME, - "Nome de Arquivo de Predefinição" + "Nome de Arquivo de Predefinio" ) MSG_HASH(MSG_INPUT_RENAME_ENTRY, - "Renomear Título" + "Renomear Ttulo" ) MSG_HASH(MSG_INTERFACE, "Interface" @@ -2845,10 +2932,10 @@ MSG_HASH(MSG_INTERNAL_STORAGE, "Armazenamento Interno" ) MSG_HASH(MSG_REMOVABLE_STORAGE, - "Armazenamento Removível" + "Armazenamento Removvel" ) MSG_HASH(MSG_INVALID_NICKNAME_SIZE, - "Tamanho de apelido inválido." + "Tamanho de apelido invlido." ) MSG_HASH(MSG_IN_BYTES, "em bytes" @@ -2860,7 +2947,7 @@ MSG_HASH(MSG_IN_MEGABYTES, "em megabytes" ) MSG_HASH(MSG_LIBRETRO_ABI_BREAK, - "foi compilado contra uma versão diferente do libretro do que esta." + "foi compilado contra uma verso diferente do libretro do que esta." ) MSG_HASH(MSG_LIBRETRO_FRONTEND, "Frontend para Libretro" @@ -2869,55 +2956,55 @@ MSG_HASH(MSG_LOADED_STATE_FROM_SLOT, "Estado de Jogo carregado do compartimento #%d." ) MSG_HASH(MSG_LOADED_STATE_FROM_SLOT_AUTO, - "Estado de Jogo carregado do compartimento #-1 (automático)." + "Estado de Jogo carregado do compartimento #-1 (automtico)." ) MSG_HASH(MSG_LOADING, "Carregando" ) MSG_HASH(MSG_FIRMWARE, - "Um ou mais arquivos de firmware estão faltando" + "Um ou mais arquivos de firmware esto faltando" ) MSG_HASH(MSG_LOADING_CONTENT_FILE, - "Carregando arquivo de conteúdo" + "Carregando arquivo de contedo" ) MSG_HASH(MSG_LOADING_HISTORY_FILE, - "Carregando arquivo de histórico" + "Carregando arquivo de histrico" ) MSG_HASH(MSG_LOADING_STATE, "Carregando Estado de Jogo" ) MSG_HASH(MSG_MEMORY, - "Memória" + "Memria" ) MSG_HASH(MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE, - "O arquivo de filme não é um arquivo BSV1 válido." + "O arquivo de filme no um arquivo BSV1 vlido." ) MSG_HASH(MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION, - "O formato de filme parece ter uma versão de serializador diferente. Provavelmente irá falhar." + "O formato de filme parece ter uma verso de serializador diferente. Provavelmente ir falhar." ) MSG_HASH(MSG_MOVIE_PLAYBACK_ENDED, - "Reprodução de filme terminou." + "Reproduo de filme terminou." ) MSG_HASH(MSG_MOVIE_RECORD_STOPPED, - "Parando a gravação de filme." + "Parando a gravao de filme." ) MSG_HASH(MSG_NETPLAY_FAILED, "Falha em inicializar o Netplay." ) MSG_HASH(MSG_NO_CONTENT_STARTING_DUMMY_CORE, - "Sem conteúdo, iniciando um núcleo modelo." + "Sem contedo, iniciando um ncleo modelo." ) MSG_HASH(MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET, - "Nenhum Estado de Jogo foi sobrescrito até o momento." + "Nenhum Estado de Jogo foi sobrescrito at o momento." ) MSG_HASH(MSG_NO_STATE_HAS_BEEN_LOADED_YET, - "Nenhum Estado de Jogo foi carregado até o momento." + "Nenhum Estado de Jogo foi carregado at o momento." ) MSG_HASH(MSG_OVERRIDES_ERROR_SAVING, - "Erro em salvar as redefinições." + "Erro em salvar as redefinies." ) MSG_HASH(MSG_OVERRIDES_SAVED_SUCCESSFULLY, - "Redefinições salvas com sucesso." + "Redefinies salvas com sucesso." ) MSG_HASH(MSG_PAUSED, "Pausado." @@ -2932,13 +3019,13 @@ MSG_HASH(MSG_RECEIVED, "recebido" ) MSG_HASH(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE, - "A gravação terminou devido ao redimensionamento." + "A gravao terminou devido ao redimensionamento." ) MSG_HASH(MSG_RECORDING_TO, "Gravando em" ) MSG_HASH(MSG_REDIRECTING_CHEATFILE_TO, - "Redirecionando o arquivo de Trapaça em" + "Redirecionando o arquivo de Trapaa em" ) MSG_HASH(MSG_REDIRECTING_SAVEFILE_TO, "Redirecionando o Jogo-Salvo em" @@ -2956,49 +3043,49 @@ MSG_HASH(MSG_REMOVED_DISK_FROM_TRAY, "Disco removido da bandeja." ) MSG_HASH(MSG_REMOVING_TEMPORARY_CONTENT_FILE, - "Removendo arquivo de conteúdo temporário" + "Removendo arquivo de contedo temporrio" ) MSG_HASH(MSG_RESET, "Reinicializar" ) MSG_HASH(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT, - "Reiniciando a gravação devido ao reinício do driver." + "Reiniciando a gravao devido ao reincio do driver." ) MSG_HASH(MSG_RESTORED_OLD_SAVE_STATE, "Estado de Jogo antigo restaurado." ) MSG_HASH(MSG_RESTORING_DEFAULT_SHADER_PRESET_TO, - "Shaders: restaurando predefinição padrão de Shader em" + "Shaders: restaurando predefinio padro de Shader em" ) MSG_HASH(MSG_REVERTING_SAVEFILE_DIRECTORY_TO, - "Revertendo diretório de Jogo-Salvo em" + "Revertendo diretrio de Jogo-Salvo em" ) MSG_HASH(MSG_REVERTING_SAVESTATE_DIRECTORY_TO, - "Revertendo diretório de Estado de Jogo em" + "Revertendo diretrio de Estado de Jogo em" ) MSG_HASH(MSG_REWINDING, - "Voltando atrás." + "Voltando atrs." ) MSG_HASH(MSG_REWIND_INIT, - "Inicializando o buffer de Voltar Atrás com tamanho" + "Inicializando o buffer de Voltar Atrs com tamanho" ) MSG_HASH(MSG_REWIND_INIT_FAILED, - "Falha em inicializar o buffer de Voltar Atrás. Voltar Atrás será desativado." + "Falha em inicializar o buffer de Voltar Atrs. Voltar Atrs ser desativado." ) MSG_HASH(MSG_REWIND_INIT_FAILED_THREADED_AUDIO, - "Esta implementação usa áudio paralelizado. Não é possível utilizar Voltar Atrás." + "Esta implementao usa udio paralelizado. No possvel utilizar Voltar Atrs." ) MSG_HASH(MSG_REWIND_REACHED_END, - "Final do buffer de Voltar Atrás atingido." + "Final do buffer de Voltar Atrs atingido." ) MSG_HASH(MSG_SAVED_NEW_CONFIG_TO, - "Nova configuração salva em" + "Nova configurao salva em" ) MSG_HASH(MSG_SAVED_STATE_TO_SLOT, "Estado de Jogo salvo no compartimento #%d." ) MSG_HASH(MSG_SAVED_STATE_TO_SLOT_AUTO, - "Estado de Jogo salvo no compartimento #-1 (automático)." + "Estado de Jogo salvo no compartimento #-1 (automtico)." ) MSG_HASH(MSG_SAVED_SUCCESSFULLY_TO, "Salvo com sucesso em" @@ -3013,40 +3100,40 @@ MSG_HASH(MSG_SCANNING, "Analisando" ) MSG_HASH(MSG_SCANNING_OF_DIRECTORY_FINISHED, - "Análise de diretório terminada" + "Anlise de diretrio terminada" ) MSG_HASH(MSG_SENDING_COMMAND, "Enviando comando" ) MSG_HASH(MSG_SEVERAL_PATCHES_ARE_EXPLICITLY_DEFINED, - "Várias modificações de conteúdo estão explicitamente definidas, ignorando todas..." + "Vrias modificaes de contedo esto explicitamente definidas, ignorando todas..." ) MSG_HASH(MSG_SHADER, "Shader" ) MSG_HASH(MSG_SHADER_PRESET_SAVED_SUCCESSFULLY, - "Predefinição de Shader salva com sucesso." + "Predefinio de Shader salva com sucesso." ) MSG_HASH(MSG_SKIPPING_SRAM_LOAD, "Ignorando carregamento da SRAM." ) MSG_HASH(MSG_SLOW_MOTION, - "Câmera Lenta." + "Cmera Lenta." ) MSG_HASH(MSG_FAST_FORWARD, - "Avanço rápido." + "Avano rpido." ) MSG_HASH(MSG_SLOW_MOTION_REWIND, - "Voltar Atrás em Câmera Lenta." + "Voltar Atrs em Cmera Lenta." ) MSG_HASH(MSG_SRAM_WILL_NOT_BE_SAVED, - "SRAM não será salva." + "SRAM no ser salva." ) MSG_HASH(MSG_STARTING_MOVIE_PLAYBACK, - "Iniciando reprodução de filme." + "Iniciando reproduo de filme." ) MSG_HASH(MSG_STARTING_MOVIE_RECORD_TO, - "Iniciando a gravação de filme em" + "Iniciando a gravao de filme em" ) MSG_HASH(MSG_STATE_SIZE, "Tamanho do Estado de Jogo" @@ -3073,16 +3160,16 @@ MSG_HASH(MSG_UNPAUSED, "Retomando." ) MSG_HASH(MSG_UNRECOGNIZED_COMMAND, - "Comando não reconhecido" + "Comando no reconhecido" ) MSG_HASH(MSG_USING_CORE_NAME_FOR_NEW_CONFIG, - "Usando o nome do núcleo para uma nova configuração." + "Usando o nome do ncleo para uma nova configurao." ) MSG_HASH(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED, - "Usando o núcleo libretro modelo. Pulando a gravação." + "Usando o ncleo libretro modelo. Pulando a gravao." ) MSG_HASH(MSG_VALUE_CONNECT_DEVICE_FROM_A_VALID_PORT, - "Conecte o dispositivo a partir de uma porta válida." + "Conecte o dispositivo a partir de uma porta vlida." ) MSG_HASH(MSG_VALUE_DISCONNECTING_DEVICE_FROM_PORT, "Desconectando o dispositivo da porta" @@ -3094,97 +3181,97 @@ MSG_HASH(MSG_VALUE_SHUTTING_DOWN, "Desligando..." ) MSG_HASH(MSG_VERSION_OF_LIBRETRO_API, - "Versão da API libretro" + "Verso da API libretro" ) MSG_HASH(MSG_VIEWPORT_SIZE_CALCULATION_FAILED, - "Falha no cálculo de tamanho da janela de exibição! Prosseguindo usando dados brutos. Isto provavelmente não funcionará corretamente..." + "Falha no clculo de tamanho da janela de exibio! Prosseguindo usando dados brutos. Isto provavelmente no funcionar corretamente..." ) MSG_HASH(MSG_VIRTUAL_DISK_TRAY, "bandeja de disco virtual." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_LATENCY, - "Latência de áudio desejada em milissegundos. Pode não ser honrado se o driver de áudio não puder prover a latência desejada." + "Latncia de udio desejada em milissegundos. Pode no ser honrado se o driver de udio no puder prover a latncia desejada." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MUTE, - "Áudio mudo/não-mudo." + "udio mudo/no-mudo." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_RATE_CONTROL_DELTA, - "Ajuda a suavizar as imperfeições na regulagem ao sincronizar áudio e vídeo. Esteja ciente que se desativado, será quase impossível de se obter a sincronia adequada." + "Ajuda a suavizar as imperfeies na regulagem ao sincronizar udio e vdeo. Esteja ciente que se desativado, ser quase impossvel de se obter a sincronia adequada." ) MSG_HASH(MENU_ENUM_SUBLABEL_CAMERA_ALLOW, - "Permitir ou não o acesso a câmera pelos núcleos." + "Permitir ou no o acesso a cmera pelos ncleos." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOCATION_ALLOW, - "Permitir ou não o acesso ao serviço de localização pelos núcleos." + "Permitir ou no o acesso ao servio de localizao pelos ncleos." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_MAX_USERS, - "Número máximo de usuários suportados pelo RetroArch." + "Nmero mximo de usurios suportados pelo RetroArch." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_POLL_TYPE_BEHAVIOR, - "Influencia como a chamada seletiva de entrada é feita dentro do RetroArch. Definindo com 'Cedo' ou 'Tarde' pode resultar em menos latência, dependendo da sua configuração." + "Influencia como a chamada seletiva de entrada feita dentro do RetroArch. Definindo com 'Cedo' ou 'Tarde' pode resultar em menos latncia, dependendo da sua configurao." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_ALL_USERS_CONTROL_MENU, - "Permitir a qualquer usuário controlar o menu. Se desabilitado, apenas o Usuário 1 poderá controlar o menu." + "Permitir a qualquer usurio controlar o menu. Se desabilitado, apenas o Usurio 1 poder controlar o menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_VOLUME, - "Volume do áudio (em dB). 0dB é o volume normal, e nenhum ganho é aplicado." + "Volume do udio (em dB). 0dB o volume normal, e nenhum ganho aplicado." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_WASAPI_EXCLUSIVE_MODE, - "Permitir ao driver WASAPI obter controle exclusivo do dispositivo de áudio. Se desativado, o modo compartilhado será utilizado." + "Permitir ao driver WASAPI obter controle exclusivo do dispositivo de udio. Se desativado, o modo compartilhado ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_WASAPI_FLOAT_FORMAT, - "Utilizar formato de ponto flutuante para o driver WASAPI, se suportado pelo dispositivo de áudio." + "Utilizar formato de ponto flutuante para o driver WASAPI, se suportado pelo dispositivo de udio." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH, - "O tamanho (em quadros) do buffer intermediário quando o driver WASAPI estiver em modo compartilhado." + "O tamanho (em quadros) do buffer intermedirio quando o driver WASAPI estiver em modo compartilhado." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_SYNC, - "Sincroniza o áudio. Recomendado." + "Sincroniza o udio. Recomendado." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_AXIS_THRESHOLD, - "Até que ponto um eixo deve ser movido para resultar em um botão pressionado." + "At que ponto um eixo deve ser movido para resultar em um boto pressionado." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_BIND_TIMEOUT, - "Quantidade de segundos para aguardar até proceder para o próximo vínculo." + "Quantidade de segundos para aguardar at proceder para o prximo vnculo." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_TURBO_PERIOD, - "Descreve o período quando os botões com turbo habilitado são alternados. Os números são descritos em quadros." + "Descreve o perodo quando os botes com turbo habilitado so alternados. Os nmeros so descritos em quadros." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_DUTY_CYCLE, - "Descreve quão longo deve ser o período de um botão com turbo habilitado. Os números são descritos como quadros." + "Descreve quo longo deve ser o perodo de um boto com turbo habilitado. Os nmeros so descritos como quadros." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VSYNC, - "Sincroniza o vídeo de saída da placa gráfica com a taxa de atualização da tela. Recomendado." + "Sincroniza o vdeo de sada da placa grfica com a taxa de atualizao da tela. Recomendado." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_ALLOW_ROTATE, - "Permite que os núcleos definam a rotação. Quando desabilitado, as requisições de rotação são ignoradas. Útil para configurações onde se rotaciona manualmente a tela." + "Permite que os ncleos definam a rotao. Quando desabilitado, as requisies de rotao so ignoradas. til para configuraes onde se rotaciona manualmente a tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_DUMMY_ON_CORE_SHUTDOWN, - "Alguns núcleos podem ter um recurso de desligamento. Se habilitado, impedirá que o núcleo feche o RetroArch. Em vez disto, carrega um núcleo modelo." + "Alguns ncleos podem ter um recurso de desligamento. Se habilitado, impedir que o ncleo feche o RetroArch. Em vez disto, carrega um ncleo modelo." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHECK_FOR_MISSING_FIRMWARE, - "Verifica se todos os firmwares necessários estão presentes antes de tentar carregar conteúdo." + "Verifica se todos os firmwares necessrios esto presentes antes de tentar carregar contedo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE, - "Taxa de atualização vertical da sua tela. Utilizado para calcular uma taxa de saída de áudio adequada. OBS: Isto será ignorado se a função 'Vídeo Paralelizado' estiver habilitada." + "Taxa de atualizao vertical da sua tela. Utilizado para calcular uma taxa de sada de udio adequada. OBS: Isto ser ignorado se a funo 'Vdeo Paralelizado' estiver habilitada." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_ENABLE, - "Habilita a saída de áudio." + "Habilita a sada de udio." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MAX_TIMING_SKEW, - "Mudança máxima na taxa de entrada de áudio. Se aumentado habilita grandes mudanças na regulagem ao custo de imprecisão no timbre do som (ex: rodando núcleos PAL em modo NTSC)." + "Mudana mxima na taxa de entrada de udio. Se aumentado habilita grandes mudanas na regulagem ao custo de impreciso no timbre do som (ex: rodando ncleos PAL em modo NTSC)." ) MSG_HASH(MSG_FAILED, "falhou" ) MSG_HASH(MSG_SUCCEEDED, - "teve êxito" + "teve xito" ) MSG_HASH(MSG_DEVICE_NOT_CONFIGURED, - "não configurado" + "no configurado" ) MSG_HASH(MSG_DEVICE_NOT_CONFIGURED_FALLBACK, - "não configurado, usando reserva" + "no configurado, usando reserva" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST, "Lista de Cursores da Base de Dados" @@ -3202,7 +3289,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ENABLED, "Habilitado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_HISTORY_PATH, - "Caminho do Histórico de Conteúdo" + "Caminho do Histrico de Contedo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_ORIGIN, "Base de Dados - Filtro : Origem" @@ -3211,61 +3298,61 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_FRANCHISE, "Base de Dados - Filtro : Franquia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_ESRB_RATING, - "Base de Dados - Filtro : Classificação ESRB" + "Base de Dados - Filtro : Classificao ESRB" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_ELSPA_RATING, - "Base de Dados - Filtro : Classificação ELSPA" + "Base de Dados - Filtro : Classificao ELSPA" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_PEGI_RATING, - "Base de Dados - Filtro : Classificação PEGI" + "Base de Dados - Filtro : Classificao PEGI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_CERO_RATING, - "Base de Dados - Filtro : Classificação CERO" + "Base de Dados - Filtro : Classificao CERO" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_BBFC_RATING, - "Base de Dados - Filtro : Classificação BBFC" + "Base de Dados - Filtro : Classificao BBFC" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_MAX_USERS, - "Base de Dados - Filtro : Usuários máximos" + "Base de Dados - Filtro : Usurios mximos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_RELEASEDATE_BY_MONTH, - "Base de Dados - Filtro : Data de Lançamento Por Mês" + "Base de Dados - Filtro : Data de Lanamento Por Ms" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_RELEASEDATE_BY_YEAR, - "Base de Dados - Filtro : Data de Lançamento Por Ano" + "Base de Dados - Filtro : Data de Lanamento Por Ano" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_EDGE_MAGAZINE_ISSUE, - "Base de Dados - Filtro : Edição da Revista Edge" + "Base de Dados - Filtro : Edio da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_EDGE_MAGAZINE_RATING, - "Base de Dados - Filtro : Classificação da Revista Edge" + "Base de Dados - Filtro : Classificao da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_DATABASE_INFO, - "Informações da Base de Dados" + "Informaes da Base de Dados" ) MSG_HASH(MSG_WIFI_SCAN_COMPLETE, - "Análise de Wi-Fi completa." + "Anlise de Wi-Fi completa." ) MSG_HASH(MSG_SCANNING_WIRELESS_NETWORKS, "Analisando redes sem fio..." ) MSG_HASH(MSG_NETPLAY_LAN_SCAN_COMPLETE, - "Análise de Netplay completa." + "Anlise de Netplay completa." ) MSG_HASH(MSG_NETPLAY_LAN_SCANNING, "Analisando por hospedeiros de Netplay..." ) MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, - "Pausar o jogo quando a janela do RetroArch não estiver ativa." + "Pausar o jogo quando a janela do RetroArch no estiver ativa." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Habilitar ou desabilitar composição (Somente no Windows)." + "Habilitar ou desabilitar composio (Somente no Windows)." ) MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, - "Habilitar ou desabilitar a lista de reprodução recente para jogos, imagens, música e vídeos." + "Habilitar ou desabilitar a lista de reproduo recente para jogos, imagens, msica e vdeos." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, - "Limita o número de itens da lista de reprodução recente para jogos, imagens, música e vídeos." + "Limita o nmero de itens da lista de reproduo recente para jogos, imagens, msica e vdeos." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_UNIFIED_MENU_CONTROLS, "Controles de Menu Unificados" @@ -3277,16 +3364,16 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FONT_ENABLE, "Exibir mensagens na tela." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_USER_REMOTE_ENABLE, - "Habilitar Remoto do Usuário %d" + "Habilitar Remoto do Usurio %d" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BATTERY_LEVEL_ENABLE, - "Exibir nível de bateria" + "Exibir nvel de bateria" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SELECT_FILE, "Selecionar Arquivo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SELECT_FROM_COLLECTION, - "Selecionar de Coleção" + "Selecionar de Coleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FILTER, "Filtro" @@ -3295,10 +3382,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SCALE, "Escala" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_WHEN_LOADED, - "O Netplay irá iniciar quando o conteúdo for carregado." + "O Netplay ir iniciar quando o contedo for carregado." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_LOAD_CONTENT_MANUALLY, - "Não foi possível encontrar um núcleo adequado ou arquivo de conteúdo, carregue manualmente." + "No foi possvel encontrar um ncleo adequado ou arquivo de contedo, carregue manualmente." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BROWSE_URL_LIST, "Navegar pela URL" @@ -3322,88 +3409,88 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ROOM_NICKNAME_LAN, "Apelido (lan): %s" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND, - "Conteúdo compatível encontrado" + "Contedo compatvel encontrado" ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_CROP_OVERSCAN, - "Corta alguns pixels ao redor das bordas da imagem habitualmente deixada em branco por desenvolvedores, que por vezes também contêm pixels de lixo." + "Corta alguns pixels ao redor das bordas da imagem habitualmente deixada em branco por desenvolvedores, que por vezes tambm contm pixels de lixo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SMOOTH, - "Adiciona um leve embaciado à imagem para suavizar as arestas da borda dos pixels. Esta opção tem pouco impacto no desempenho." + "Adiciona um leve embaciado imagem para suavizar as arestas da borda dos pixels. Esta opo tem pouco impacto no desempenho." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FILTER, - "Aplica um filtro de vídeo processado pela CPU. OBS: Pode vir a um alto custo de desempenho. Alguns filtros de vídeo podem funcionar apenas para núcleos que usam cores de 32 bits ou 16 bits." + "Aplica um filtro de vdeo processado pela CPU. OBS: Pode vir a um alto custo de desempenho. Alguns filtros de vdeo podem funcionar apenas para ncleos que usam cores de 32 bits ou 16 bits." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_USERNAME, - "Insira o nome de usuário de sua conta Retro Achievements." + "Insira o nome de usurio de sua conta Retro Achievements." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_PASSWORD, "Insira a senha de sua conta Retro Achievements." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_NICKNAME, - "Insira seu nome de usuário aqui. Isto será utilizado para sessões do Netplay, entre outras coisas." + "Insira seu nome de usurio aqui. Isto ser utilizado para sesses do Netplay, entre outras coisas." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_POST_FILTER_RECORD, - "Capturar a imagem depois que os filtros (mas não os Shaders) forem aplicados. Seu vídeo ficará tão elegante quanto o que você vê na tela." + "Capturar a imagem depois que os filtros (mas no os Shaders) forem aplicados. Seu vdeo ficar to elegante quanto o que voc v na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_LIST, - "Selecionar qual núcleo utilizar." + "Selecionar qual ncleo utilizar." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOAD_CONTENT_LIST, - "Selecionar qual conteúdo iniciar." + "Selecionar qual contedo iniciar." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETWORK_INFORMATION, - "Exibir interfaces de rede e endereços de IP associados." + "Exibir interfaces de rede e endereos de IP associados." ) MSG_HASH(MENU_ENUM_SUBLABEL_SYSTEM_INFORMATION, - "Exibir informações específicas do dispositivo." + "Exibir informaes especficas do dispositivo." ) MSG_HASH(MENU_ENUM_SUBLABEL_QUIT_RETROARCH, "Sair do programa." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_WINDOW_WIDTH, - "Define a largura personalizada para a janela de exibição. Deixado em 0 a janela irá dimensionar o mais largo possível." + "Define a largura personalizada para a janela de exibio. Deixado em 0 a janela ir dimensionar o mais largo possvel." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_WINDOW_HEIGHT, - "Define a altura personalizada para a janela de exibição. Deixado em 0 a janela irá dimensionar o mais alto possível." + "Define a altura personalizada para a janela de exibio. Deixado em 0 a janela ir dimensionar o mais alto possvel." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FULLSCREEN_X, - "Define a largura personalizada para o modo de tela cheia em não-janela. Deixar em 0 irá usar a resolução da área de trabalho." + "Define a largura personalizada para o modo de tela cheia em no-janela. Deixar em 0 ir usar a resoluo da rea de trabalho." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FULLSCREEN_Y, - "Define a altura personalizada para o modo de tela cheia em não-janela. Deixar em 0 irá usar a resolução da área de trabalho." + "Define a altura personalizada para o modo de tela cheia em no-janela. Deixar em 0 ir usar a resoluo da rea de trabalho." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MESSAGE_POS_X, - "Especifique a posição personalizada no eixo X para o texto na tela." + "Especifique a posio personalizada no eixo X para o texto na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MESSAGE_POS_Y, - "Especifique a posição personalizada no eixo Y para o texto na tela." + "Especifique a posio personalizada no eixo Y para o texto na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FONT_SIZE, "Especifique o tamanho da fonte em pontos." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_HIDE_IN_MENU, - "Ocultar a Transparência enquanto estiver dentro do menu e exibir novamente ao sair." + "Ocultar a Transparncia enquanto estiver dentro do menu e exibir novamente ao sair." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS, - "Exibir comandos de teclado/controle na transparência." + "Exibir comandos de teclado/controle na transparncia." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS_PORT, - "Selecione a porta para a transparência escutar se Exibir Comandos na Transparência estiver habilitado." + "Selecione a porta para a transparncia escutar se Exibir Comandos na Transparncia estiver habilitado." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_COLLECTION_LIST, - "O conteúdo analisado aparecerá aqui." + "O contedo analisado aparecer aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SCALE_INTEGER, - "Apenas dimensiona o vídeo em valores inteiros. O tamanho de base depende da geometria relatada pelo sistema e da proporção de tela. Se 'Forçar Proporção' não estiver definido, X / Y serão dimensionados independentemente em valores inteiros." + "Apenas dimensiona o vdeo em valores inteiros. O tamanho de base depende da geometria relatada pelo sistema e da proporo de tela. Se 'Forar Proporo' no estiver definido, X / Y sero dimensionados independentemente em valores inteiros." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_GPU_SCREENSHOT, - "Captura a tela com Shader de GPU se disponível." + "Captura a tela com Shader de GPU se disponvel." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_ROTATION, - "Força uma certa rotação da tela. A rotação é adicionada a rotação que o núcleo definir." + "Fora uma certa rotao da tela. A rotao adicionada a rotao que o ncleo definir." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FORCE_SRGB_DISABLE, - "Desabilita de forma forçada o suporte sRGB FBO. Alguns drivers Intel OpenGL no Windows possuem problemas de vídeo com o suporte sRGB FBO se estiver habilitado. Habilitando isto pode contornar o problema." + "Desabilita de forma forada o suporte sRGB FBO. Alguns drivers Intel OpenGL no Windows possuem problemas de vdeo com o suporte sRGB FBO se estiver habilitado. Habilitando isto pode contornar o problema." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FULLSCREEN, "Inicia em tela cheia. Pode ser mudado a qualquer momento." @@ -3412,88 +3499,58 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_WINDOWED_FULLSCREEN, "Se estiver em tela cheia, prefira utilizar uma janela de tela cheia." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_GPU_RECORD, - "Grava o material de saída do Shader de GPU se disponível." + "Grava o material de sada do Shader de GPU se disponvel." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_INDEX, - "Ao criar um Estado de Jogo, o índice do Estado de Jogo é aumentado automaticamente antes de ser salvo. Ao carregar um conteúdo, o índice será definido para o índice mais alto existente." + "Ao criar um Estado de Jogo, o ndice do Estado de Jogo aumentado automaticamente antes de ser salvo. Ao carregar um contedo, o ndice ser definido para o ndice mais alto existente." ) MSG_HASH(MENU_ENUM_SUBLABEL_BLOCK_SRAM_OVERWRITE, "Bloqueia a SRAM de ser sobrescrita ao carregar um Estado de Jogo. Pode causar problemas no jogo." ) MSG_HASH(MENU_ENUM_SUBLABEL_FASTFORWARD_RATIO, - "A taxa máxima na qual o conteúdo será executado quando utilizado o Avanço Rápido (ex: 5.0x para conteúdos em 60fps = 300 fps max). Se for definido como 0.0x, a taxa de Avanço Rápido é ilimitada (sem FPS max)." + "A taxa mxima na qual o contedo ser executado quando utilizado o Avano Rpido (ex: 5.0x para contedos em 60fps = 300 fps max). Se for definido como 0.0x, a taxa de Avano Rpido ilimitada (sem FPS max)." ) MSG_HASH(MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO, - "Quando está em Câmera Lenta, o conteúdo será diminuído pelo fator especificado/definido." + "Quando est em Cmera Lenta, o contedo ser diminudo pelo fator especificado/definido." ) MSG_HASH(MENU_ENUM_SUBLABEL_REWIND_ENABLE, - "Habilita Voltar Atrás. Isso irá impactar o desempenho ao jogar." + "Habilita Voltar Atrs. Isso ir impactar o desempenho ao jogar." ) MSG_HASH(MENU_ENUM_SUBLABEL_REWIND_GRANULARITY, - "Ao definir um número de quadros para Voltar Atrás, você pode retroceder vários quadros de uma só vez, aumentando a velocidade da função." + "Ao definir um nmero de quadros para Voltar Atrs, voc pode retroceder vrios quadros de uma s vez, aumentando a velocidade da funo." ) MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_LOG_LEVEL, - "Define o nível de registro de eventos para os núcleos. Se o nível do registro enviado por um núcleo for abaixo deste valor, o mesmo é ignorado." + "Define o nvel de registro de eventos para os ncleos. Se o nvel do registro enviado por um ncleo for abaixo deste valor, o mesmo ignorado." ) MSG_HASH(MENU_ENUM_SUBLABEL_PERFCNT_ENABLE, - "Habilitar os contadores de desempenho para o RetroArch (e núcleos)." + "Habilitar os contadores de desempenho para o RetroArch (e ncleos)." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_SAVE, - "Cria automaticamente um Estado de Jogo no final da execução do RetroArch. O RetroArch irá carregar automaticamente este Estado de Jogo se a função 'Autocarregar Estado de Jogo' estiver habilitada." + "Cria automaticamente um Estado de Jogo no final da execuo do RetroArch. O RetroArch ir carregar automaticamente este Estado de Jogo se a funo 'Autocarregar Estado de Jogo' estiver habilitada." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_LOAD, - "Autocarrega o último Estado de Jogo autosalvo na inicialização do RetroArch." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_SAVESTATE_THUMBNAIL_ENABLE, - "Exibe miniaturas dos Estados de Jogo salvos dentro do menu." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_AUTOSAVE_INTERVAL, - "Salva automaticamente a SRAM não volátil em um intervalo regular. Isto é desativado por padrão a menos que seja definido de outra forma. O intervalo é medido em segundos. O valor 0 desativa o salvamento automático." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE, - "Se habilitado, substitui os vínculos de entrada com os vínculos remapeados definidos pelo núcleo atual." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_AUTODETECT_ENABLE, - "Habilita a detecção automática de entrada. Isto tentará configurar automaticamente Joypads no estilo 'Plug-and-Play'." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_MENU_INPUT_SWAP_OK_CANCEL, - "Inverte os botões para OK/Cancelar. Desabilitado é o estilo japonês, habilitado é o estilo ocidental." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_LIBRETRO, - "Se desabilitado, o conteúdo continuará rodando em segundo plano quando o menu do RetroArch é alternado." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DRIVER, - "Driver de vídeo a ser utilizado." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_DRIVER, - "Driver de áudio a ser utilizado." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_DRIVER, - "Driver de entrada a ser utilizado. Dependendo do driver de vídeo, pode forçar um driver de entrada diferente." - ) -MSG_HASH(MENU_ENUM_SUBLABEL_JOYPAD_DRIVER, - "Driver de Joypad a ser utilizado." + "Autocarrega o ltimo Estado de Jogo autosalvo na inicializao do RetroArch." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_DRIVER, - "Driver de reamostragem de áudio a ser utilizado." + "Driver de reamostragem de udio a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_CAMERA_DRIVER, - "Driver de câmera a ser utilizado." + "Driver de cmera a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOCATION_DRIVER, - "Driver de localização a ser utilizado." + "Driver de localizao a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_DRIVER, "Driver de menu a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_RECORD_DRIVER, - "Driver de gravação a ser utilizado." + "Driver de gravao a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_DRIVER, "Driver de WiFi a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE, - "Filtra os arquivos em exibição no explorador de arquivos por extensões suportadas." + "Filtra os arquivos em exibio no explorador de arquivos por extenses suportadas." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_WALLPAPER, "Seleciona uma imagem para definir como plano de fundo do menu." @@ -3502,58 +3559,58 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DYNAMIC_WALLPAPER, "Carrega dinamicamente um novo plano de fundo dependendo do contexto." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_DEVICE, - "Substitui o dispositivo de áudio padrão utilizado pelo driver de áudio. Isto depende do driver." + "Substitui o dispositivo de udio padro utilizado pelo driver de udio. Isto depende do driver." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_DSP_PLUGIN, - "Plugin DSP de Áudio que processa o áudio antes de ser enviado para o driver." + "Plugin DSP de udio que processa o udio antes de ser enviado para o driver." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_RATE, - "Taxa de amostragem da saída de áudio." + "Taxa de amostragem da sada de udio." ) MSG_HASH(MENU_ENUM_SUBLABEL_OVERLAY_OPACITY, - "Opacidade de todos os elementos de interface da Transparência." + "Opacidade de todos os elementos de interface da Transparncia." ) MSG_HASH(MENU_ENUM_SUBLABEL_OVERLAY_SCALE, - "Escala de todos os elementos de interface da Transparência." + "Escala de todos os elementos de interface da Transparncia." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_ENABLE, - "Habilita a Transparência." + "Habilita a Transparncia." ) MSG_HASH(MENU_ENUM_SUBLABEL_OVERLAY_PRESET, - "Seleciona uma Transparência pelo navegador de arquivos." + "Seleciona uma Transparncia pelo navegador de arquivos." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_IP_ADDRESS, - "Endereço do hospedeiro a se conectar." + "Endereo do hospedeiro a se conectar." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_TCP_UDP_PORT, - "Porta do endereço de IP do hospedeiro. Pode ser uma porta TCP ou uma porta UDP." + "Porta do endereo de IP do hospedeiro. Pode ser uma porta TCP ou uma porta UDP." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_PASSWORD, "Senha para conectar ao hospedeiro de Netplay. Utilizado apenas no modo hospedeiro." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_PUBLIC_ANNOUNCE, - "Anunciar os jogos de Netplay publicamente. Se não for definido, os clientes deverão conectar manualmente em vez de usar o lobby público." + "Anunciar os jogos de Netplay publicamente. Se no for definido, os clientes devero conectar manualmente em vez de usar o lobby pblico." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_SPECTATE_PASSWORD, - "Senha para conectar ao hospedeiro de Netplay apenas com privilégios de espectador. Utilizado apenas no modo hospedeiro." + "Senha para conectar ao hospedeiro de Netplay apenas com privilgios de espectador. Utilizado apenas no modo hospedeiro." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_START_AS_SPECTATOR, "Define se o Netplay deve iniciar em modo espectador." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_ALLOW_SLAVES, - "Define se conexões em modo escravo são permitidas. Clientes em modo escravo requerem muito pouco poder de processamento em ambos os lados, mas irão sofrer significamente da latência de rede." + "Define se conexes em modo escravo so permitidas. Clientes em modo escravo requerem muito pouco poder de processamento em ambos os lados, mas iro sofrer significamente da latncia de rede." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REQUIRE_SLAVES, - "Define se conexões que não estão em modo escravo são proibidas. Não recomendado, exceto para redes muito rápidas com máquinas muito lentas." + "Define se conexes que no esto em modo escravo so proibidas. No recomendado, exceto para redes muito rpidas com mquinas muito lentas." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_STATELESS_MODE, - "Define se deve executar o Netplay em modo que não utilize Estados de Jogo. Se definido como verdadeiro, uma rede muito rápida é necessária, mas Voltar Atrás não é permitido, então não haverá oscilação no Netplay." + "Define se deve executar o Netplay em modo que no utilize Estados de Jogo. Se definido como verdadeiro, uma rede muito rpida necessria, mas Voltar Atrs no permitido, ento no haver oscilao no Netplay." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CHECK_FRAMES, - "Frequência em quadros no qual o Netplay verificará se o hospedeiro e o cliente estão sincronizados." + "Frequncia em quadros no qual o Netplay verificar se o hospedeiro e o cliente esto sincronizados." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_NAT_TRAVERSAL, - "Ao hospedar uma partida, tente receber conexões da Internet pública usando UPnP ou tecnologias similares para escapar das redes locais." + "Ao hospedar uma partida, tente receber conexes da Internet pblica usando UPnP ou tecnologias similares para escapar das redes locais." ) MSG_HASH(MENU_ENUM_SUBLABEL_STDIN_CMD_ENABLE, "Habilitar interface de comando stdin." @@ -3566,15 +3623,19 @@ MSG_HASH(MENU_ENUM_SUBLABEL_POINTER_ENABLE, ) MSG_HASH(MENU_ENUM_SUBLABEL_THUMBNAILS, "Tipo de miniatura a ser exibida." - ) + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_LEFT_THUMBNAILS, + "Tipo de miniatura para exibir esquerda." + ) MSG_HASH(MENU_ENUM_SUBLABEL_TIMEDATE_ENABLE, "Exibir data e/ou hora atuais dentro do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_BATTERY_LEVEL_ENABLE, - "Exibir o nível de bateria atual dentro do menu." + "Exibir o nvel de bateria atual dentro do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_NAVIGATION_WRAPAROUND, - "Voltar ao início ou final se o limite da lista for alcançado horizontalmente ou verticalmente." + "Voltar ao incio ou final se o limite da lista for alcanado horizontalmente ou verticalmente." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_ENABLE_HOST, "Habilita o Netplay no modo hospedeiro (servidor)." @@ -3583,42 +3644,42 @@ MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_ENABLE_CLIENT, "Habilita o Netplay no modo cliente." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_DISCONNECT, - "Desconecta de uma conexão de Netplay ativa." + "Desconecta de uma conexo de Netplay ativa." ) MSG_HASH(MENU_ENUM_SUBLABEL_SCAN_DIRECTORY, - "Analisa um diretório por arquivos compatíveis e os adiciona à coleção." + "Analisa um diretrio por arquivos compatveis e os adiciona coleo." ) MSG_HASH(MENU_ENUM_SUBLABEL_SCAN_FILE, - "Analisa um arquivo compatível e o adiciona à coleção." + "Analisa um arquivo compatvel e o adiciona coleo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SWAP_INTERVAL, - "Usa um intervalo de troca personalizado para V-Sync. Defina para reduzir efetivamente a taxa de atualização do monitor pela metade." + "Usa um intervalo de troca personalizado para V-Sync. Defina para reduzir efetivamente a taxa de atualizao do monitor pela metade." ) MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, - "Ordenar os Jogos-Salvos em pastas com o nome do núcleo utilizado." + "Ordenar os Jogos-Salvos em pastas com o nome do ncleo utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, - "Ordenar os Estados de Jogo em pastas com o nome do núcleo utilizado." + "Ordenar os Estados de Jogo em pastas com o nome do ncleo utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REQUEST_DEVICE_I, "Solicitar jogar com o dispositivo de entrada dado.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, - "URL para o diretório de atualização de núcleos no buildbot do Libreto." + "URL para o diretrio de atualizao de ncleos no buildbot do Libreto." ) MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, - "URL para o diretório de atualizações de recursos no buildbot do Libretro." + "URL para o diretrio de atualizaes de recursos no buildbot do Libretro." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE, - "Após o download, extrair automaticamente os arquivos contidos nos arquivos comprimidos baixados." + "Aps o download, extrair automaticamente os arquivos contidos nos arquivos comprimidos baixados." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REFRESH_ROOMS, "Analisar por novas salas." ) MSG_HASH(MENU_ENUM_SUBLABEL_DELETE_ENTRY, - "Remover esta entrada da coleção." + "Remover esta entrada da coleo." ) MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION, - "Visualizar mais informações sobre o conteúdo." + "Visualizar mais informaes sobre o contedo." ) MSG_HASH(MENU_ENUM_SUBLABEL_ADD_TO_FAVORITES, "Adicionar o item aos seus favoritos." @@ -3627,22 +3688,22 @@ MSG_HASH(MENU_ENUM_SUBLABEL_ADD_TO_FAVORITES_PLAYLIST, "Adicionar o item aos seus favoritos." ) MSG_HASH(MENU_ENUM_SUBLABEL_RUN, - "Iniciar o conteúdo." + "Iniciar o contedo." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_FILE_BROWSER_SETTINGS, - "Ajustar as definições do navegador de arquivos." + "Ajustar as definies do navegador de arquivos." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUTO_REMAPS_ENABLE, - "Habilitar por padrão controles personalizados na inicialização." + "Habilitar por padro controles personalizados na inicializao." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUTO_OVERRIDES_ENABLE, - "Habilitar por padrão configuração personalizada na inicialização." + "Habilitar por padro configurao personalizada na inicializao." ) MSG_HASH(MENU_ENUM_SUBLABEL_GAME_SPECIFIC_OPTIONS, - "Habilitar por padrão opções de núcleo personalizadas na inicialização." + "Habilitar por padro opes de ncleo personalizadas na inicializao." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_ENABLE, - "Exibir o nome do núcleo atual dentro do menu." + "Exibir o nome do ncleo atual dentro do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_DATABASE_MANAGER, "Visualizar bases de dados." @@ -3654,7 +3715,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_TAKE_SCREENSHOT, "Capturar uma imagem da tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_CLOSE_CONTENT, - "Fecha o conteúdo atual. Alterações não salvas serão perdidas." + "Fecha o contedo atual. Alteraes no salvas sero perdidas." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOAD_STATE, "Carregar um Estado de Jogo do compartimento selecionado atualmente." @@ -3663,88 +3724,88 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_STATE, "Salvar um Estado de Jogo no compartimento selecionado atualmente." ) MSG_HASH(MENU_ENUM_SUBLABEL_RESUME, - "Retomar a execução do conteúdo atual e sair do Menu Rápido." + "Retomar a execuo do contedo atual e sair do Menu Rpido." ) MSG_HASH(MENU_ENUM_SUBLABEL_RESUME_CONTENT, - "Retomar a execução do conteúdo atual e sair do Menu Rápido." + "Retomar a execuo do contedo atual e sair do Menu Rpido." ) MSG_HASH(MENU_ENUM_SUBLABEL_STATE_SLOT, "Altera o compartimento do Estado de Jogo selecionado atualmente." ) MSG_HASH(MENU_ENUM_SUBLABEL_UNDO_LOAD_STATE, - "Se um Estado de Jogo for carregado, o conteúdo voltará ao estado anterior ao carregamento." + "Se um Estado de Jogo for carregado, o contedo voltar ao estado anterior ao carregamento." ) MSG_HASH(MENU_ENUM_SUBLABEL_UNDO_SAVE_STATE, - "Se o Estado de Jogo foi sobrescrito, ele voltará ao Estado de Jogo salvo anteriormente." + "Se o Estado de Jogo foi sobrescrito, ele voltar ao Estado de Jogo salvo anteriormente." ) MSG_HASH(MENU_ENUM_SUBLABEL_ACCOUNTS_RETRO_ACHIEVEMENTS, - "Serviço Retro Achievements. Para mais informações, visite http://retroachievements.org (em inglês)" + "Servio Retro Achievements. Para mais informaes, visite http://retroachievements.org (em ingls)" ) MSG_HASH(MENU_ENUM_SUBLABEL_ACCOUNTS_LIST, "Gerenciar contas configuradas atualmente." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_META_REWIND, - "Gerenciar as configurações de Voltar Atrás." + "Gerenciar as configuraes de Voltar Atrs." ) MSG_HASH(MENU_ENUM_SUBLABEL_RESTART_CONTENT, - "Reinicia o conteúdo do começo." + "Reinicia o contedo do comeo." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE, - "Salva um arquivo de redefinição de configuração que será aplicado a todo o conteúdo carregado por este núcleo. Terá prioridade sobre a configuração principal." + "Salva um arquivo de redefinio de configurao que ser aplicado a todo o contedo carregado por este ncleo. Ter prioridade sobre a configurao principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_CURRENT_CONFIG_OVERRIDE_GAME, - "Salva um arquivo de redefinição de configuração que será aplicado apenas ao conteúdo atual. Terá prioridade sobre a configuração principal." + "Salva um arquivo de redefinio de configurao que ser aplicado apenas ao contedo atual. Ter prioridade sobre a configurao principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_CHEAT_OPTIONS, - "Configurar códigos de Trapaça." + "Configurar cdigos de Trapaa." ) MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_OPTIONS, - "Configurar Shader para realçar a aparência da imagem." + "Configurar Shader para realar a aparncia da imagem." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_INPUT_REMAPPING_OPTIONS, - "Alterar os controles para o conteúdo que está sendo executado." + "Alterar os controles para o contedo que est sendo executado." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_OPTIONS, - "Alterar as opções para o conteúdo que está sendo executado." + "Alterar as opes para o contedo que est sendo executado." ) MSG_HASH(MENU_ENUM_SUBLABEL_SHOW_ADVANCED_SETTINGS, - "Exibir as configurações avançadas para usuários experientes (oculto por padrão)." + "Exibir as configuraes avanadas para usurios experientes (oculto por padro)." ) MSG_HASH(MENU_ENUM_SUBLABEL_THREADED_DATA_RUNLOOP_ENABLE, "Executar tarefas em linhas de processamento paralelas." ) MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_ENTRY_REMOVE, - "Permitir que o usuário possa remover itens das coleções." + "Permitir que o usurio possa remover itens das colees." ) MSG_HASH(MENU_ENUM_SUBLABEL_SYSTEM_DIRECTORY, - "Define o diretório de sistema. Os núcleos podem consultar este diretório para carregar arquivos de BIOS, configurações específicas do sistema, etc." + "Define o diretrio de sistema. Os ncleos podem consultar este diretrio para carregar arquivos de BIOS, configuraes especficas do sistema, etc." ) MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_BROWSER_DIRECTORY, - "Define o diretório inicial do navegador de arquivos." + "Define o diretrio inicial do navegador de arquivos." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_DIR, "Usualmente definido por desenvolvedores que agrupam aplicativos Libretro/RetroArch para apontar para os recursos." ) MSG_HASH(MENU_ENUM_SUBLABEL_DYNAMIC_WALLPAPERS_DIRECTORY, - "Diretório para armazenar planos de fundo dinamicamente carregados pelo menu dependendo do contexto." + "Diretrio para armazenar planos de fundo dinamicamente carregados pelo menu dependendo do contexto." ) MSG_HASH(MENU_ENUM_SUBLABEL_THUMBNAILS_DIRECTORY, - "Miniaturas auxiliares (arte da embalagem/imagens diversas e etc.) são armazenadas aqui." + "Miniaturas auxiliares (arte da embalagem/imagens diversas e etc.) so armazenadas aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_CONFIG_DIRECTORY, - "Define o diretório inicial para o navegador de configurações do menu." + "Define o diretrio inicial para o navegador de configuraes do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN, - "O número de quadros de latência de entrada para o Netplay utilizar para mascarar a latência da rede. Reduz a oscilação e torna o Netplay menos intensivo para a CPU, ao custo de atraso perceptível na entrada." + "O nmero de quadros de latncia de entrada para o Netplay utilizar para mascarar a latncia da rede. Reduz a oscilao e torna o Netplay menos intensivo para a CPU, ao custo de atraso perceptvel na entrada." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, - "O intervalo de quadros de latência de entrada que pode ser utilizado para mascarar a latência da rede. Reduz a oscilação e torna o Netplay menos intensivo para a CPU, ao custo de atraso imprevisível na entrada." + "O intervalo de quadros de latncia de entrada que pode ser utilizado para mascarar a latncia da rede. Reduz a oscilao e torna o Netplay menos intensivo para a CPU, ao custo de atraso imprevisvel na entrada." ) MSG_HASH(MENU_ENUM_SUBLABEL_DISK_CYCLE_TRAY_STATUS, - "Alterna o disco atual. Se o disco estiver inserido, o mesmo será ejetado. Se o disco não estiver inserido, o mesmo será inserido." + "Alterna o disco atual. Se o disco estiver inserido, o mesmo ser ejetado. Se o disco no estiver inserido, o mesmo ser inserido." ) MSG_HASH(MENU_ENUM_SUBLABEL_DISK_INDEX, - "Mudar o índice do disco." + "Mudar o ndice do disco." ) MSG_HASH(MENU_ENUM_SUBLABEL_DISK_OPTIONS, "Gerenciamento de imagem de disco." @@ -3753,13 +3814,13 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Selecione uma imagem de disco para inserir." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, - "Certifica-se de que a taxa de quadros é controlada enquanto estiver dentro do menu." + "Certifica-se de que a taxa de quadros controlada enquanto estiver dentro do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, - "Selecionar um tema diferente para os ícones. As alterações terão efeito após reiniciar o programa." + "Selecionar um tema diferente para os cones. As alteraes tero efeito aps reiniciar o programa." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, - "Habilitar as sombras para todos os ícones. Isto terá um pequeno impacto no desempenho." + "Habilitar as sombras para todos os cones. Isto ter um pequeno impacto no desempenho." ) MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_COLOR_THEME, "Selecionar um tema de gradiente de cor de plano de fundo diferente." @@ -3771,7 +3832,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_XMB_MENU_COLOR_THEME, "Selecionar um tema de gradiente de cor de plano de fundo diferente." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_RIBBON_ENABLE, - "Selecionar um efeito de plano de fundo animado. Pode exigir mais processamento da GPU dependendo do efeito. Se o desempenho for insatisfatório, desligue este efeito ou reverta para um efeito mais simples." + "Selecionar um efeito de plano de fundo animado. Pode exigir mais processamento da GPU dependendo do efeito. Se o desempenho for insatisfatrio, desligue este efeito ou reverta para um efeito mais simples." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_FONT, "Selecionar uma fonte principal diferente para ser usada pelo menu." @@ -3783,167 +3844,167 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_IMAGES, "Exibir a aba de imagem dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_MUSIC, - "Exibir a aba de música dentro do menu principal." + "Exibir a aba de msica dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_VIDEO, - "Exibir a aba de vídeo dentro do menu principal." + "Exibir a aba de vdeo dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_NETPLAY, "Exibir a aba de Netplay dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_SETTINGS, - "Mostrar a aba de configurações dentro do menu principal." + "Mostrar a aba de configuraes dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, - "Mostrar a aba de histórico recente dentro do menu principal." + "Mostrar a aba de histrico recente dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, - "Mostrar a aba de importação de conteúdo dentro do menu principal." + "Mostrar a aba de importao de contedo dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, - "Exibir a tela inicial no menu. É automaticamente definido como falso após o programa iniciar pela primeira vez." + "Exibir a tela inicial no menu. automaticamente definido como falso aps o programa iniciar pela primeira vez." ) MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, - "Modificar a opacidade do gráfico do cabeçalho." + "Modificar a opacidade do grfico do cabealho." ) MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_FOOTER_OPACITY, - "Modificar a opacidade do gráfico do rodapé." + "Modificar a opacidade do grfico do rodap." ) MSG_HASH(MENU_ENUM_SUBLABEL_DPI_OVERRIDE_ENABLE, - "O menu normalmente se dimensiona dinamicamente. Se você desejar definir uma escala de tamanho específica em vez disto, habilite esta função." + "O menu normalmente se dimensiona dinamicamente. Se voc desejar definir uma escala de tamanho especfica em vez disto, habilite esta funo." ) MSG_HASH(MENU_ENUM_SUBLABEL_DPI_OVERRIDE_VALUE, - "Definir o tamanho do dimensionamento personalizado aqui. OBS: Você deve habilitar a função 'Redefinição de DPI' para que este dimensionamento tenha efeito." + "Definir o tamanho do dimensionamento personalizado aqui. OBS: Voc deve habilitar a funo 'Redefinio de DPI' para que este dimensionamento tenha efeito." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_ASSETS_DIRECTORY, - "Salvar todos os arquivos baixados neste diretório." + "Salvar todos os arquivos baixados neste diretrio." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_REMAPPING_DIRECTORY, - "Salvar todos os controles remapeados neste diretório." + "Salvar todos os controles remapeados neste diretrio." ) MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_DIR_PATH, - "Diretório onde o programa busca por conteúdo/núcleos." + "Diretrio onde o programa busca por contedo/ncleos." ) MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_INFO_PATH, - "Os arquivos de informação do aplicativo/núcleo são armazenados aqui." + "Os arquivos de informao do aplicativo/ncleo so armazenados aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_JOYPAD_AUTOCONFIG_DIR, - "Se um Joypad estiver conectado, o mesmo será autoconfigurado se um arquivo de configuração correspondente estiver presente dento deste diretório." + "Se um Joypad estiver conectado, o mesmo ser autoconfigurado se um arquivo de configurao correspondente estiver presente dento deste diretrio." ) MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_DIRECTORY, - "Salvar todas as coleções neste diretório." + "Salvar todas as colees neste diretrio." ) MSG_HASH(MENU_ENUM_SUBLABEL_CACHE_DIRECTORY, - "Se for definido um diretório, o conteúdo que é temporariamente extraído (ex: dos arquivos) sera extraído para este diretório." + "Se for definido um diretrio, o contedo que temporariamente extrado (ex: dos arquivos) sera extrado para este diretrio." ) MSG_HASH(MENU_ENUM_SUBLABEL_CURSOR_DIRECTORY, - "As consultas salvas são armazenadas neste diretório." + "As consultas salvas so armazenadas neste diretrio." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_DATABASE_DIRECTORY, - "As bases de dados são armazenadas neste diretório." + "As bases de dados so armazenadas neste diretrio." ) MSG_HASH(MENU_ENUM_SUBLABEL_ASSETS_DIRECTORY, - "Esta localização é consultada por padrão quando a interface do menu tenta procurar por recursos carregáveis, etc." + "Esta localizao consultada por padro quando a interface do menu tenta procurar por recursos carregveis, etc." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVEFILE_DIRECTORY, - "Salvar todos os Jogos-Salvos neste diretório. Se não for definido, tentaremos salvar dentro do diretório de trabalho do arquivo." + "Salvar todos os Jogos-Salvos neste diretrio. Se no for definido, tentaremos salvar dentro do diretrio de trabalho do arquivo." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVESTATE_DIRECTORY, - "Salvar todos os Estados de Jogo neste diretório. Se não for definido, tentaremos salvar dentro do diretório de trabalho do arquivo." + "Salvar todos os Estados de Jogo neste diretrio. Se no for definido, tentaremos salvar dentro do diretrio de trabalho do arquivo." ) MSG_HASH(MENU_ENUM_SUBLABEL_SCREENSHOT_DIRECTORY, - "Diretório para armazenar as capturas de tela." + "Diretrio para armazenar as capturas de tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_OVERLAY_DIRECTORY, - "Define um diretório onde as Transparências são mantidas para fácil acesso." + "Define um diretrio onde as Transparncias so mantidas para fcil acesso." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_DATABASE_PATH, - "Os arquivos de Trapaça são mantidos aqui." + "Os arquivos de Trapaa so mantidos aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_FILTER_DIR, - "Diretório onde os arquivos de filtro DSP de áudio são mantidos." + "Diretrio onde os arquivos de filtro DSP de udio so mantidos." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FILTER_DIR, - "Diretório onde os arquivos de filtro de vídeo processado por CPU são mantidos." + "Diretrio onde os arquivos de filtro de vdeo processado por CPU so mantidos." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_DIR, - "Define um diretório onde os arquivos de Shader de vídeo processado por GPU são mantidos para fácil acesso." + "Define um diretrio onde os arquivos de Shader de vdeo processado por GPU so mantidos para fcil acesso." ) MSG_HASH(MENU_ENUM_SUBLABEL_RECORDING_OUTPUT_DIRECTORY, - "As gravações serão armazenadas neste diretório." + "As gravaes sero armazenadas neste diretrio." ) MSG_HASH(MENU_ENUM_SUBLABEL_RECORDING_CONFIG_DIRECTORY, - "As configurações de gravação serão mantidas aqui." + "As configuraes de gravao sero mantidas aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FONT_PATH, - "Selecionar uma fonte diferente para as notificações na tela." + "Selecionar uma fonte diferente para as notificaes na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_APPLY_CHANGES, - "As alterações das configurações de Shader terão efeito imediato. Use isto se você alterou a quantidade de estágios de Shader, filtros, escala FBO, etc." + "As alteraes das configuraes de Shader tero efeito imediato. Use isto se voc alterou a quantidade de estgios de Shader, filtros, escala FBO, etc." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_NUM_PASSES, - "Aumentar ou diminuir a quantidade de estágios do pipeline de Shader. Você pode adicionar um Shader separado para cada estágio do pipeline e configurar sua escala e filtro." + "Aumentar ou diminuir a quantidade de estgios do pipeline de Shader. Voc pode adicionar um Shader separado para cada estgio do pipeline e configurar sua escala e filtro." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET, - "Carregar uma predefinição de Shader. O pipeline de Shader será definido automaticamente." + "Carregar uma predefinio de Shader. O pipeline de Shader ser definido automaticamente." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_AS, - "Salvar as definições de Shader atuais como uma nova predefinição de Shader." + "Salvar as definies de Shader atuais como uma nova predefinio de Shader." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_CORE, - "Salvar as definições de Shader atuais como a definição padrão para esta aplicação/núcleo." + "Salvar as definies de Shader atuais como a definio padro para esta aplicao/ncleo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_GAME, - "Salvar as definições de Shader atuais como a definição padrão para o conteúdo." + "Salvar as definies de Shader atuais como a definio padro para o contedo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PARAMETERS, - "Modifica diretamente o Shader atual. As alterações não serão salvas no arquivo de predefinição." + "Modifica diretamente o Shader atual. As alteraes no sero salvas no arquivo de predefinio." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_PARAMETERS, - "Modifica a predefinição de Shader atualmente utilizada no menu." + "Modifica a predefinio de Shader atualmente utilizada no menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_NUM_PASSES, - "Aumentar ou diminuir a quantidade de Trapaças." + "Aumentar ou diminuir a quantidade de Trapaas." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_APPLY_CHANGES, - "As alterações de Trapaça terão efeito imediato." + "As alteraes de Trapaa tero efeito imediato." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_FILE_LOAD, - "Carregar um arquivo de Trapaça." + "Carregar um arquivo de Trapaa." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_FILE_SAVE_AS, - "Salvar as Trapaças atuais como um arquivo de Jogo-Salvo." + "Salvar as Trapaas atuais como um arquivo de Jogo-Salvo." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SETTINGS, - "Acessar rapidamente todas as configurações relevantes ao jogo." + "Acessar rapidamente todas as configuraes relevantes ao jogo." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_INFORMATION, - "Visualizar informações sobre a aplicação/núcleo." + "Visualizar informaes sobre a aplicao/ncleo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_ASPECT_RATIO, - "Valor em ponto flutuante da proporção de tela (largura / altura), utilizado se Proporção de Tela estiver definido como 'Configuração'." + "Valor em ponto flutuante da proporo de tela (largura / altura), utilizado se Proporo de Tela estiver definido como 'Configurao'." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_HEIGHT, - "Personalizar a altura da janela de exibição que é usada se a Proporção de Tela estiver definida como 'Personalizada'." + "Personalizar a altura da janela de exibio que usada se a Proporo de Tela estiver definida como 'Personalizada'." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_WIDTH, - "Personalizar a largura da janela de exibição que é usada se a Proporção de Tela estiver definida como 'Personalizada'." + "Personalizar a largura da janela de exibio que usada se a Proporo de Tela estiver definida como 'Personalizada'." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_X, - "Deslocamento personalizado no eixo-X da janela de exibição. Será ignorado se a 'Escala em Inteiros' estiver habilitada. Neste caso ela será centralizada automaticamente." + "Deslocamento personalizado no eixo-X da janela de exibio. Ser ignorado se a 'Escala em Inteiros' estiver habilitada. Neste caso ela ser centralizada automaticamente." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_Y, - "Deslocamento personalizado no eixo-Y da janela de exibição. Será ignorado se a 'Escala em Inteiros' estiver habilitada. Neste caso ela será centralizada automaticamente." + "Deslocamento personalizado no eixo-Y da janela de exibio. Ser ignorado se a 'Escala em Inteiros' estiver habilitada. Neste caso ela ser centralizada automaticamente." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_USE_MITM_SERVER, "Utilizar Servidor MITM" ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_USE_MITM_SERVER, - "Encaminhar conexões do Netplay através de um servidor 'homem no meio' (MITM). Útil se o hospedeiro estiver atrás de um firewall ou tiver problemas de NAT/UPnP.") + "Encaminhar conexes do Netplay atravs de um servidor 'homem no meio' (MITM). til se o hospedeiro estiver atrs de um firewall ou tiver problemas de NAT/UPnP.") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_MITM_SERVER, - "Localização do Servidor de Retransmissão") + "Localizao do Servidor de Retransmisso") MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_MITM_SERVER, - "Escolha um servidor de retransmissão específico para usar. Locais geograficamente mais próximos tendem a ter menor latência.") + "Escolha um servidor de retransmisso especfico para usar. Locais geograficamente mais prximos tendem a ter menor latncia.") MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_MIXER, "Adicionar ao mixer" ) @@ -3951,49 +4012,49 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_MIXER_AND_COLLECTION, "Adicionar ao mixer" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FILTER_BY_CURRENT_CORE, - "Filtrar por núcleo atual" + "Filtrar por ncleo atual" ) MSG_HASH(MSG_AUDIO_MIXER_VOLUME, - "Volume global do mixer de áudio" + "Volume global do mixer de udio" ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MIXER_VOLUME, - "Volume global do mixer de áudio (em dB). 0dB é o volume normal, e nenhum ganho será aplicado." + "Volume global do mixer de udio (em dB). 0dB o volume normal, e nenhum ganho ser aplicado." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_VOLUME, - "Nível de Volume do Mixer de Áudio (dB)" + "Nvel de Volume do Mixer de udio (dB)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_MUTE, - "Mixer de Áudio Mudo" + "Mixer de udio Mudo" ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MIXER_MUTE, - "Mixer de áudio mudo/não-mudo." + "Mixer de udio mudo/no-mudo." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_ONLINE_UPDATER, "Exibir Atualizador Online" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_ONLINE_UPDATER, - "Exibir a opção 'Atualizador Online'." + "Exibir a opo 'Atualizador Online'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_VIEWS_SETTINGS, - "Visualizações") + "Visualizaes") MSG_HASH( MENU_ENUM_SUBLABEL_MENU_VIEWS_SETTINGS, "Exibir elementos na tela de menu." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_CORE_UPDATER, - "Exibir Atualizador de Núcleos" + "Exibir Atualizador de Ncleos" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_CORE_UPDATER, - "Exibir a opção de atualizar núcleos (e arquivos de informação de núcleo)." + "Exibir a opo de atualizar ncleos (e arquivos de informao de ncleo)." ) MSG_HASH(MSG_PREPARING_FOR_CONTENT_SCAN, - "Preparando a busca de conteúdo..." + "Preparando a busca de contedo..." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_DELETE, - "Remover núcleo" + "Remover ncleo" ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_DELETE, - "Remover este núcleo do disco.") + "Remover este ncleo do disco.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_FRAMEBUFFER_OPACITY, "Opacidade do Framebuffer" ) @@ -4004,13 +4065,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES, "Favoritos" ) MSG_HASH(MENU_ENUM_SUBLABEL_GOTO_FAVORITES, - "Conteúdo adicionado aos 'Favoritos' vai aparecer aqui." + "Contedo adicionado aos 'Favoritos' vai aparecer aqui." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GOTO_MUSIC, - "Músicas" + "Msicas" ) MSG_HASH(MENU_ENUM_SUBLABEL_GOTO_MUSIC, - "Músicas que foram reproduzidas aparecem aqui." + "Msicas que foram reproduzidas aparecem aqui." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GOTO_IMAGES, "Imagens" @@ -4019,22 +4080,22 @@ MSG_HASH(MENU_ENUM_SUBLABEL_GOTO_IMAGES, "Imagens que foram exibidas aparecem aqui." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GOTO_VIDEO, - "Vídeos" + "Vdeos" ) MSG_HASH(MENU_ENUM_SUBLABEL_GOTO_VIDEO, - "Vídeos que foram reproduzidos aparecem aqui." + "Vdeos que foram reproduzidos aparecem aqui." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_ICONS_ENABLE, - "Ícones do Menu" + "cones do Menu" ) MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_ICONS_ENABLE, - "Habilitar/desabilitar os ícones exibidos do lado esquerdo dos itens de menu." + "Habilitar/desabilitar os cones exibidos do lado esquerdo dos itens de menu." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MAIN_MENU_ENABLE_SETTINGS, - "Habilitar guia de configurações" + "Habilitar guia de configuraes" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS_PASSWORD, - "Definir senha para habilitar guia de configurações" + "Definir senha para habilitar guia de configuraes" ) MSG_HASH(MSG_INPUT_ENABLE_SETTINGS_PASSWORD, "Digite a senha" @@ -4046,160 +4107,160 @@ MSG_HASH(MSG_INPUT_ENABLE_SETTINGS_PASSWORD_NOK, "Senha incorreta." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_MAIN_MENU_ENABLE_SETTINGS, - "Habilita a guia de configurações. É necessário reiniciar para que a guia apareça." + "Habilita a guia de configuraes. necessrio reiniciar para que a guia aparea." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_SETTINGS_PASSWORD, - "O fornecimento de uma senha ao ocultar a guia de configurações permite restaurar mais tarde a partir do menu, indo para a guia Menu Principal, selecionando Habilitar guia configurações e inserindo a senha." + "O fornecimento de uma senha ao ocultar a guia de configuraes permite restaurar mais tarde a partir do menu, indo para a guia Menu Principal, selecionando Habilitar guia configuraes e inserindo a senha." ) MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_ENTRY_RENAME, - "Permita que o usuário renomeie as entradas nas coleções." + "Permita que o usurio renomeie as entradas nas colees." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_ENTRY_RENAME, "Permitir renomear entradas" ) MSG_HASH(MENU_ENUM_SUBLABEL_RENAME_ENTRY, - "Renomear o título da entrada.") + "Renomear o ttulo da entrada.") MSG_HASH(MENU_ENUM_LABEL_VALUE_RENAME_ENTRY, "Renomear") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_LOAD_CORE, - "Exibir Carregar Núcleo" + "Exibir Carregar Ncleo" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_LOAD_CORE, - "Exibir/ocultar a opção 'Carregar Núcleo'." + "Exibir/ocultar a opo 'Carregar Ncleo'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_LOAD_CONTENT, - "Exibir Carregar Conteúdo" + "Exibir Carregar Contedo" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_LOAD_CONTENT, - "Exibir/ocultar a opção 'Carregar Conteúdo'." + "Exibir/ocultar a opo 'Carregar Contedo'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_INFORMATION, - "Exibir Informação" + "Exibir Informao" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_INFORMATION, - "Exibir/ocultar a opção 'Informação'." + "Exibir/ocultar a opo 'Informao'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_CONFIGURATIONS, - "Exibir Configurações" + "Exibir Configuraes" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_CONFIGURATIONS, - "Exibir/ocultar a opção 'Configurações'." + "Exibir/ocultar a opo 'Configuraes'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_HELP, "Exibir Ajuda" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_HELP, - "Exibir/ocultar a opção 'Ajuda'." + "Exibir/ocultar a opo 'Ajuda'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_QUIT_RETROARCH, "Exibir Sair do RetroArch" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_QUIT_RETROARCH, - "Exibir/ocultar a opção 'Sair do RetroArch'." + "Exibir/ocultar a opo 'Sair do RetroArch'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_REBOOT, "Exibir Reiniciar" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_REBOOT, - "Exibir/ocultar a opção 'Reiniciar'." + "Exibir/ocultar a opo 'Reiniciar'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_VIEWS_SETTINGS, - "Menu Rápido" + "Menu Rpido" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_VIEWS_SETTINGS, - "Exibir ou ocultar elementos na tela de Menu Rápido." + "Exibir ou ocultar elementos na tela de Menu Rpido." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_TAKE_SCREENSHOT, "Exibir Captura de Tela" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_TAKE_SCREENSHOT, - "Exibir/ocultar a opção 'Captura de Tela'." + "Exibir/ocultar a opo 'Captura de Tela'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SAVE_LOAD_STATE, "Exibir Salvar/Carregar Estado" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_LOAD_STATE, - "Exibir/ocultar as opções para salvar/carregar estados." + "Exibir/ocultar as opes para salvar/carregar estados." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_UNDO_SAVE_LOAD_STATE, "Exibir Desfazer Salvar/Carregar Estado" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_UNDO_SAVE_LOAD_STATE, - "Exibir/ocultar as opções para abolir o salvar/carregar estado." + "Exibir/ocultar as opes para abolir o salvar/carregar estado." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_ADD_TO_FAVORITES, "Exibir Adicionar aos Favoritos" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_ADD_TO_FAVORITES, - "Exibir/ocultar a opção 'Adicionar aos Favoritos'." + "Exibir/ocultar a opo 'Adicionar aos Favoritos'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_OPTIONS, - "Exibir Opções" + "Exibir Opes" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_OPTIONS, - "Exibir/ocultar a opção 'Opções'." + "Exibir/ocultar a opo 'Opes'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_CONTROLS, "Exibir Controles" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_CONTROLS, - "Exibir/ocultar a opção 'Controles'." + "Exibir/ocultar a opo 'Controles'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_CHEATS, - "Exibir Trapaças" + "Exibir Trapaas" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_CHEATS, - "Exibir/ocultar a opção 'Trapaças'." + "Exibir/ocultar a opo 'Trapaas'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SHADERS, "Exibir Shaders" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SHADERS, - "Exibir/ocultar a opção 'Shaders'." + "Exibir/ocultar a opo 'Shaders'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SAVE_CORE_OVERRIDES, - "Exibir Salvar Redefinição de Núcleo" + "Exibir Salvar Redefinio de Ncleo" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_CORE_OVERRIDES, - "Exibir/ocultar a opção 'Salvar Redefinição de Núcleo'." + "Exibir/ocultar a opo 'Salvar Redefinio de Ncleo'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SAVE_GAME_OVERRIDES, - "Exibir Salvar Redefinição de Jogo" + "Exibir Salvar Redefinio de Jogo" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_GAME_OVERRIDES, - "Exibir/ocultar a opção 'Salvar Redefinição de Jogo'." + "Exibir/ocultar a opo 'Salvar Redefinio de Jogo'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_INFORMATION, - "Exibir Informação" + "Exibir Informao" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_INFORMATION, - "Exibir/ocultar a opção 'Informação'.") + "Exibir/ocultar a opo 'Informao'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_ENABLE, - "Ativar Notificação de Fundo") + "Ativar Notificao de Fundo") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_RED, - "Notificação de Fundo em Cor Vermelha") + "Notificao de Fundo em Cor Vermelha") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_GREEN, - "Notificação de Fundo em Cor Verde") + "Notificao de Fundo em Cor Verde") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_BLUE, - "Notificação de Fundo em Cor Azul") + "Notificao de Fundo em Cor Azul") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_OPACITY, - "Opacidade da Notificação de Fundo") + "Opacidade da Notificao de Fundo") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_DISABLE_KIOSK_MODE, "Desabilitar o Modo Quiosque" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_DISABLE_KIOSK_MODE, - "Desabilita o Modo Quiosque. É necessária uma reinicialização para que a mudança tenha total efeito." + "Desabilita o Modo Quiosque. necessria uma reinicializao para que a mudana tenha total efeito." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_ENABLE_KIOSK_MODE, "Habilitar o Modo Quiosque" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENABLE_KIOSK_MODE, - "Protege a configuração escondendo todas as configurações relacionadas à configuração." + "Protege a configurao escondendo todas as configuraes relacionadas configurao." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_KIOSK_MODE_PASSWORD, "Definir senha para desabilitar o Modo Quiosque" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_KIOSK_MODE_PASSWORD, - "Fornecer uma senha ao habilitar o Modo Quiosque tornando possível desabilitar mais tarde a partir do menu, indo para o Menu Principal, selecionando Desabilitar o Modo Quiosque e inserindo a senha." + "Fornecer uma senha ao habilitar o Modo Quiosque tornando possvel desabilitar mais tarde a partir do menu, indo para o Menu Principal, selecionando Desabilitar o Modo Quiosque e inserindo a senha." ) MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD, "Digite a Senha" @@ -4210,38 +4271,38 @@ MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD_OK, MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD_NOK, "Senha incorreta.") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_RED, - "Notificação em Cor Vermelha") + "Notificao em Cor Vermelha") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_GREEN, - "Notificação em Cor Verde") + "Notificao em Cor Verde") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_BLUE, - "Notificação em Cor Azul") + "Notificao em Cor Azul") MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAMECOUNT_SHOW, "Mostrar contagem de quadros na tela FPS") MSG_HASH(MSG_CONFIG_OVERRIDE_LOADED, - "Substituição de configuração carregada.") + "Substituio de configurao carregada.") MSG_HASH(MSG_GAME_REMAP_FILE_LOADED, "Arquivo de remapeamento do jogo carregado.") MSG_HASH(MSG_CORE_REMAP_FILE_LOADED, "Arquivo de remapeamento principal carregado.") MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTOMATICALLY_ADD_CONTENT_TO_PLAYLIST, - "Adicione automaticamente conteúdo à lista de reprodução") + "Adicione automaticamente contedo lista de reproduo") MSG_HASH(MENU_ENUM_SUBLABEL_AUTOMATICALLY_ADD_CONTENT_TO_PLAYLIST, - "Verifica automaticamente o conteúdo carregado para que eles apareçam dentro das listas de reprodução.") + "Verifica automaticamente o contedo carregado para que eles apaream dentro das listas de reproduo.") MSG_HASH(MSG_SCANNING_OF_FILE_FINISHED, - "Verificação do arquivo terminado") + "Verificao do arquivo terminado") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_OPACITY, "Opacidade da Janela") MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_QUALITY, - "Qualidade da Reamostragem do Áudio") + "Qualidade da Reamostragem do udio") MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_QUALITY, - "Abaixe esse valor para favorecer o desempenho/baixa latência em relação à qualidade de áudio, aumente se desejar melhor qualidade de áudio à custa do desempenho/baixa latência.") + "Abaixe esse valor para favorecer o desempenho/baixa latncia em relao qualidade de udio, aumente se desejar melhor qualidade de udio custa do desempenho/baixa latncia.") MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_WATCH_FOR_CHANGES, - "Ver arquivos de shader para mudanças") + "Ver arquivos de shader para mudanas") MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_WATCH_FOR_CHANGES, - "Aplicar automaticamente as alterações feitas nos arquivos de shader no disco.") + "Aplicar automaticamente as alteraes feitas nos arquivos de shader no disco.") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_SHOW_DECORATIONS, - "Mostrar Decorações da Janela") + "Mostrar Decoraes da Janela") MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, - "Exibir estatísticas") + "Exibir estatsticas") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, - "Mostrar estatísticas técnicas na tela.") + "Mostrar estatsticas tcnicas na tela.") From 13a9d11a2c8c5e7413b417d846d164aee85a2de3 Mon Sep 17 00:00:00 2001 From: radius Date: Tue, 10 Apr 2018 21:52:27 -0500 Subject: [PATCH 260/517] remap-redux: fix potential overrun --- menu/cbs/menu_cbs_get_value.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 215162cf77..6557e3f982 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -618,8 +618,7 @@ static void menu_action_setting_disp_set_label_input_desc_kbd( remap_id = settings->uints.input_keymapper_ids[offset][id]; - for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) + for (key_id = 0; key_id < RARCH_MAX_KEYS; key_id++) { if(remap_id == key_descriptors[key_id].key) break; From 372603858d610de66c1d69a75181f629799fe1af Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:10:51 +0200 Subject: [PATCH 261/517] Revert "(Menu) Update menu code" This reverts commit f481924f1e9b3a59211df2cc99d094daf0a7ae3f. --- menu/cbs/menu_cbs_ok.c | 4 ++-- menu/drivers/menu_generic.c | 2 +- menu/widgets/menu_dialog.c | 18 ++++++++++++++++-- menu/widgets/menu_dialog.h | 5 ++++- menu/widgets/menu_input_dialog.c | 10 ++++------ menu/widgets/menu_input_dialog.h | 3 +-- network/netplay/netplay_handshake.c | 8 +------- setting_list.c | 5 +---- 8 files changed, 30 insertions(+), 25 deletions(-) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index a4eb7b1395..d19808ae75 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -2020,7 +2020,7 @@ static int (funcname)(void *data, const char *path, const char *label, unsigned line.type = type; \ line.idx = (_idx); \ line.cb = _cb; \ - if (!menu_input_dialog_start(&line, data)) \ + if (!menu_input_dialog_start(&line)) \ return -1; \ return 0; \ } @@ -3986,7 +3986,7 @@ static int action_ok_netplay_enable_client(void *data, line.idx = 0; line.cb = action_ok_netplay_enable_client_hostname_cb; - if (menu_input_dialog_start(&line, data)) + if (menu_input_dialog_start(&line)) return 0; #endif return -1; diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index aa091bdd10..3add7400e2 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -238,7 +238,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); /* Have to defer it so we let settings refresh. */ - menu_dialog_push(menu); + menu_dialog_push(); } break; } diff --git a/menu/widgets/menu_dialog.c b/menu/widgets/menu_dialog.c index 0c42557094..23aebda618 100644 --- a/menu/widgets/menu_dialog.c +++ b/menu/widgets/menu_dialog.c @@ -252,7 +252,7 @@ void menu_dialog_push_pending(bool push, enum menu_dialog_type type) menu_dialog_active = true; } -void menu_dialog_push(void *data) +void menu_dialog_push(void) { menu_displaylist_info_t info; const char *label = NULL; @@ -271,7 +271,9 @@ void menu_dialog_push(void *data) if (label) info.label = strdup(label); - menu_displaylist_ctl(DISPLAYLIST_HELP, &info, data); + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + menu_displaylist_ctl(DISPLAYLIST_HELP, &info, menu); } void menu_dialog_set_current_id(unsigned id) @@ -289,6 +291,18 @@ void menu_dialog_reset(void) menu_display_toggle_set_reason(MENU_TOGGLE_REASON_NONE); } +void menu_dialog_show_message( + enum menu_dialog_type type, enum msg_hash_enums msg) +{ + menu_dialog_current_msg = msg; + + if (!menu_driver_ctl(RARCH_MENU_CTL_IS_TOGGLE, NULL)) + menu_display_toggle_set_reason(MENU_TOGGLE_REASON_MESSAGE); + + menu_dialog_push_pending(true, type); + menu_dialog_push(); +} + bool menu_dialog_is_active(void) { return menu_dialog_active; diff --git a/menu/widgets/menu_dialog.h b/menu/widgets/menu_dialog.h index 0df01e8202..b692fe3138 100644 --- a/menu/widgets/menu_dialog.h +++ b/menu/widgets/menu_dialog.h @@ -59,10 +59,13 @@ void menu_dialog_unset_pending_push(void); bool menu_dialog_is_push_pending(void); -void menu_dialog_push(void *data); +void menu_dialog_push(void); void menu_dialog_reset(void); +void menu_dialog_show_message( + enum menu_dialog_type type, enum msg_hash_enums msg); + bool menu_dialog_is_active(void); void menu_dialog_set_current_id(unsigned id); diff --git a/menu/widgets/menu_input_dialog.c b/menu/widgets/menu_input_dialog.c index ca33931fd9..79af78a1a3 100644 --- a/menu/widgets/menu_input_dialog.c +++ b/menu/widgets/menu_input_dialog.c @@ -119,13 +119,12 @@ bool menu_input_dialog_start_search(void *data) return true; } -bool menu_input_dialog_start(menu_input_ctx_line_t *line, - void *data) +bool menu_input_dialog_start(menu_input_ctx_line_t *line) { - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; if (!line) return false; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return false; menu_input_dialog_display_kb(); @@ -136,8 +135,7 @@ bool menu_input_dialog_start(menu_input_ctx_line_t *line, sizeof(menu_input_dialog_keyboard_label)); if (line->label_setting) strlcpy(menu_input_dialog_keyboard_label_setting, - line->label_setting, - sizeof(menu_input_dialog_keyboard_label_setting)); + line->label_setting, sizeof(menu_input_dialog_keyboard_label_setting)); menu_input_dialog_keyboard_type = line->type; menu_input_dialog_keyboard_idx = line->idx; diff --git a/menu/widgets/menu_input_dialog.h b/menu/widgets/menu_input_dialog.h index 0c4b8187a0..cb6cc8e3ca 100644 --- a/menu/widgets/menu_input_dialog.h +++ b/menu/widgets/menu_input_dialog.h @@ -55,8 +55,7 @@ void menu_input_dialog_display_kb(void); bool menu_input_dialog_get_display_kb(void); -bool menu_input_dialog_start(menu_input_ctx_line_t *line, - void *data); +bool menu_input_dialog_start(menu_input_ctx_line_t *line); void menu_input_dialog_end(void); diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c index 48ceedfbf0..021b51d35c 100644 --- a/network/netplay/netplay_handshake.c +++ b/network/netplay/netplay_handshake.c @@ -37,7 +37,6 @@ #include "../../version.h" #ifdef HAVE_MENU -#include "../../menu/menu_driver.h" #include "../../menu/widgets/menu_input_dialog.h" #endif @@ -416,8 +415,6 @@ bool netplay_handshake_init(netplay_t *netplay, { #ifdef HAVE_MENU menu_input_ctx_line_t line; - menu_handle_t *menu = NULL; - rarch_menu_running(); #endif @@ -428,10 +425,7 @@ bool netplay_handshake_init(netplay_t *netplay, line.label = msg_hash_to_str(MSG_NETPLAY_ENTER_PASSWORD); line.label_setting = "no_setting"; line.cb = handshake_password; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (menu && !menu_input_dialog_start(&line, menu)) + if (!menu_input_dialog_start(&line)) return false; #endif } diff --git a/setting_list.c b/setting_list.c index eb7047360d..254bd62e38 100644 --- a/setting_list.c +++ b/setting_list.c @@ -2004,7 +2004,6 @@ static void menu_input_st_hex_cb(void *userdata, const char *str) static int setting_generic_action_ok_linefeed(void *data, bool wraparound) { menu_input_ctx_line_t line; - menu_handle_t *menu = NULL; input_keyboard_line_complete_t cb = NULL; rarch_setting_t *setting = (rarch_setting_t*)data; @@ -2035,9 +2034,7 @@ static int setting_generic_action_ok_linefeed(void *data, bool wraparound) line.idx = 0; line.cb = cb; - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (!menu || !menu_input_dialog_start(&line, menu)) + if (!menu_input_dialog_start(&line)) return -1; return 0; From 13d7e2bed2aa8907376a2654f381f12afe1854d1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:10:59 +0200 Subject: [PATCH 262/517] Revert "(Zarch) Buildfix" This reverts commit a3175a8fa3c4127b78582315f4082bcf8adf3bfc. --- menu/drivers/zarch.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/menu/drivers/zarch.c b/menu/drivers/zarch.c index 444fe5861a..609341055f 100644 --- a/menu/drivers/zarch.c +++ b/menu/drivers/zarch.c @@ -531,11 +531,7 @@ static int zarch_zui_render_lay_root_recent( tabbed->tabline_size + j * ZUI_ITEM_SIZE_PX, rich_label, i, entry_value, gamepad_index == (signed)i)) { - menu_handle_t *menu = (menu_handle_t*)NULL; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (menu_entry_action(&entry, menu, i, MENU_ACTION_OK)) + if (menu_entry_action(&entry, i, MENU_ACTION_OK)) { menu_entry_free(&entry); if (!string_is_empty(rich_label)) @@ -1088,7 +1084,7 @@ static bool zarch_load_image(void *userdata, &zui->textures.bg); break; case MENU_IMAGE_THUMBNAIL: - case MENU_IMAGE_LEFT_THUMBNAIL: + break; case MENU_IMAGE_SAVESTATE_THUMBNAIL: /* TODO/FIXME -implement */ break; @@ -1097,10 +1093,10 @@ static bool zarch_load_image(void *userdata, return true; } -static void zarch_context_reset(void *data, void *userdata, bool is_threaded) +static void zarch_context_reset(void *data, bool is_threaded) { settings_t *settings = config_get_ptr(); - zui_t *zui = (zui_t*)userdata; + zui_t *zui = (zui_t*)data; if (!zui || !settings) return; @@ -1122,7 +1118,6 @@ static int zarch_iterate(void *data, void *userdata, enum menu_action action) { int ret; menu_entry_t entry; - menu_handle_t *menu = (menu_handle_t*)data; zui_t *zui = (zui_t*)userdata; size_t selection = menu_navigation_get_selection(); @@ -1134,7 +1129,7 @@ static int zarch_iterate(void *data, void *userdata, enum menu_action action) zui->action = action; - ret = menu_entry_action(data, &entry, selection, action); + ret = menu_entry_action(&entry, selection, action); menu_entry_free(&entry); if (ret) return -1; @@ -1161,11 +1156,11 @@ static bool zarch_menu_init_list(void *data) info.list = selection_buf; - if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, &info, data)) + if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, &info)) { - bool ret = false; + bool ret = false; info.need_push = true; - ret = menu_displaylist_process(&info); + ret = menu_displaylist_process(&info); menu_displaylist_info_free(&info); return ret; } From 4993630e10ae8411103818bed7cd3e1fe973fdd4 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:11:04 +0200 Subject: [PATCH 263/517] Revert "Cleanups" This reverts commit 8d6067b1d43b96028c61f9d710bfab7bba6c9295. --- menu/widgets/menu_entry.c | 2 +- menu/widgets/menu_input_dialog.c | 10 +++++----- menu/widgets/menu_input_dialog.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index c4299fd107..e7f26d8f7f 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -482,7 +482,7 @@ int menu_entry_action(menu_entry_t *entry, entry->label, entry->type, i); break; case MENU_ACTION_SEARCH: - menu_input_dialog_start_search(data); + menu_input_dialog_start_search(); break; case MENU_ACTION_SCAN: diff --git a/menu/widgets/menu_input_dialog.c b/menu/widgets/menu_input_dialog.c index 79af78a1a3..1017c4c4d5 100644 --- a/menu/widgets/menu_input_dialog.c +++ b/menu/widgets/menu_input_dialog.c @@ -99,16 +99,16 @@ void menu_input_dialog_hide_kb(void) menu_input_dialog_keyboard_display = false; } -bool menu_input_dialog_start_search(void *data) +bool menu_input_dialog_start_search(void) { - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl( + RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return false; menu_input_dialog_display_kb(); - strlcpy(menu_input_dialog_keyboard_label, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), + strlcpy(menu_input_dialog_keyboard_label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), sizeof(menu_input_dialog_keyboard_label)); input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_LINE_FREE, NULL); diff --git a/menu/widgets/menu_input_dialog.h b/menu/widgets/menu_input_dialog.h index cb6cc8e3ca..7cc048bb4f 100644 --- a/menu/widgets/menu_input_dialog.h +++ b/menu/widgets/menu_input_dialog.h @@ -47,7 +47,7 @@ unsigned menu_input_dialog_get_kb_type(void); unsigned menu_input_dialog_get_kb_idx(void); -bool menu_input_dialog_start_search(void *data); +bool menu_input_dialog_start_search(void); void menu_input_dialog_hide_kb(void); From 87c9d9eb9a833bfad9fc57e88f051429749ba7a9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:11:10 +0200 Subject: [PATCH 264/517] Revert "Cleanups" This reverts commit 8632a925682d258a746b8921421140f733b06f2f. --- menu/widgets/menu_input_bind_dialog.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/menu/widgets/menu_input_bind_dialog.c b/menu/widgets/menu_input_bind_dialog.c index b9ee32a6f7..2fc050aa01 100644 --- a/menu/widgets/menu_input_bind_dialog.c +++ b/menu/widgets/menu_input_bind_dialog.c @@ -81,12 +81,12 @@ static bool menu_input_key_bind_custom_bind_keyboard_cb( } static int menu_input_key_bind_set_mode_common( - menu_handle_t *menu, enum menu_input_binds_ctl_state state, rarch_setting_t *setting) { menu_displaylist_info_t info; unsigned bind_type = 0; + menu_handle_t *menu = NULL; struct retro_keybind *keybind = NULL; unsigned index_offset = setting->index_offset; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); @@ -116,6 +116,8 @@ static int menu_input_key_bind_set_mode_common( info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND)); + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info, menu)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); @@ -132,6 +134,8 @@ static int menu_input_key_bind_set_mode_common( info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND_ALL)); + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info, menu)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); @@ -259,7 +263,7 @@ bool menu_input_key_bind_set_mode( return false; if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return false; - if (menu_input_key_bind_set_mode_common(menu, state, setting) == -1) + if (menu_input_key_bind_set_mode_common(state, setting) == -1) return false; index_offset = setting->index_offset; From 21244c6ec7ffc4c5879f8d4f2bc1af18e0dfadd4 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:11:18 +0200 Subject: [PATCH 265/517] Revert "(Menu) Get rid of more RARCH_MENU_CTL_DRIVER_DATA_GET calls" This reverts commit cb3b5d72abe87f95585f1aa460de5c97abdf407f. --- menu/drivers/menu_generic.c | 2 +- menu/menu_input.c | 21 ++++++++++++++++----- menu/menu_input.h | 3 +-- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 3add7400e2..2424c888bd 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -254,7 +254,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) } if (BIT64_GET(menu->state, MENU_STATE_POST_ITERATE)) - menu_input_post_iterate(menu, &ret, action); + menu_input_post_iterate(&ret, action); end: if (ret) diff --git a/menu/menu_input.c b/menu/menu_input.c index a3929ce78f..a791ad83d3 100644 --- a/menu/menu_input.c +++ b/menu/menu_input.c @@ -210,7 +210,6 @@ static int menu_input_mouse_post_iterate(uint64_t *input_mouse, } static int menu_input_mouse_frame( - menu_handle_t *menu, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) { @@ -288,6 +287,10 @@ static int menu_input_mouse_frame( if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_BUTTON_R)) { size_t selection = menu_navigation_get_selection(); + menu_handle_t *menu = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + menu_entry_action(entry, menu, (unsigned)selection, MENU_ACTION_CANCEL); @@ -412,7 +415,6 @@ int16_t menu_input_mouse_state(enum menu_input_mouse_state state) } static int menu_input_pointer_post_iterate( - menu_handle_t *menu, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) { @@ -526,6 +528,10 @@ static int menu_input_pointer_post_iterate( if (menu_input->pointer.counter > 32) { size_t selection = menu_navigation_get_selection(); + menu_handle_t *menu = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + if (cbs && cbs->action_start) return menu_entry_action(entry, menu, (unsigned)selection, @@ -558,7 +564,11 @@ static int menu_input_pointer_post_iterate( { if (!pointer_oldback) { + menu_handle_t *menu = NULL; pointer_oldback = true; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + menu_entry_action(entry, menu, (unsigned)menu_navigation_get_selection(), MENU_ACTION_CANCEL); @@ -570,7 +580,7 @@ static int menu_input_pointer_post_iterate( return ret; } -void menu_input_post_iterate(void *data, int *ret, unsigned action) +void menu_input_post_iterate(int *ret, unsigned action) { menu_entry_t entry; settings_t *settings = config_get_ptr(); @@ -582,10 +592,11 @@ void menu_input_post_iterate(void *data, int *ret, unsigned action) menu_entry_init(&entry); menu_entry_get(&entry, 0, selection, NULL, false); - *ret = menu_input_mouse_frame(data, cbs, &entry, action); + *ret = menu_input_mouse_frame(cbs, &entry, action); if (settings->bools.menu_pointer_enable) - *ret |= menu_input_pointer_post_iterate(data, cbs, &entry, action); + *ret |= menu_input_pointer_post_iterate(cbs, &entry, action); menu_entry_free(&entry); } + diff --git a/menu/menu_input.h b/menu/menu_input.h index 1dd5dc669b..2c5694eb00 100644 --- a/menu/menu_input.h +++ b/menu/menu_input.h @@ -108,8 +108,7 @@ typedef struct menu_input_ctx_hitbox int32_t y2; } menu_input_ctx_hitbox_t; -void menu_input_post_iterate(void *data, - int *ret, unsigned action); +void menu_input_post_iterate(int *ret, unsigned action); int16_t menu_input_pointer_state(enum menu_input_pointer_state state); From a07ada7af60985e1bab5746c51db3e6804b98a35 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:11:23 +0200 Subject: [PATCH 266/517] Revert "More cleanups - getting rid of RARCH_MENU_CTL_DRIVER_DATA_GET" This reverts commit a8f7d99abaf3adefcee466013c035bd9fe4b0ec7. --- menu/cbs/menu_cbs_info.c | 13 +++--- menu/cbs/menu_cbs_left.c | 5 +-- menu/cbs/menu_cbs_ok.c | 2 +- menu/cbs/menu_cbs_right.c | 5 +-- menu/cbs/menu_cbs_select.c | 82 +++++++++++++++++++++++--------------- menu/cbs/menu_cbs_start.c | 62 ++++++++++------------------ menu/menu_entries.h | 7 ++-- menu/menu_setting.c | 15 ++++--- menu/menu_setting.h | 9 ++--- menu/widgets/menu_entry.c | 6 +-- 10 files changed, 100 insertions(+), 106 deletions(-) diff --git a/menu/cbs/menu_cbs_info.c b/menu/cbs/menu_cbs_info.c index bd880cd83d..f6d673a212 100644 --- a/menu/cbs/menu_cbs_info.c +++ b/menu/cbs/menu_cbs_info.c @@ -30,10 +30,10 @@ #include "../../network/netplay/netplay_discovery.h" #endif -static int action_info_default(void *data, unsigned type, const char *label) +static int action_info_default(unsigned type, const char *label) { menu_displaylist_info_t info; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); size_t selection = menu_navigation_get_selection(); @@ -45,6 +45,8 @@ static int action_info_default(void *data, unsigned type, const char *label) info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_INFO_SCREEN)); + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + if (!menu_displaylist_ctl(DISPLAYLIST_HELP, &info, menu)) goto error; @@ -66,14 +68,15 @@ int generic_action_ok_help(void *data, const char *label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums id, enum menu_dialog_type id2); -static int action_info_cheevos(void *data, - unsigned type, const char *label) +static int action_info_cheevos(unsigned type, const char *label) { - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; unsigned new_id = type - MENU_SETTINGS_CHEEVOS_START; menu_dialog_set_current_id(new_id); + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + return generic_action_ok_help(menu, NULL, label, new_id, 0, 0, MENU_ENUM_LABEL_CHEEVOS_DESCRIPTION, MENU_DIALOG_HELP_CHEEVOS_DESCRIPTION); diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index aa1d0c45f3..322d10ff0b 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -553,7 +553,7 @@ static int action_left_shader_filter_default(void *data, MENU_ENUM_LABEL_VIDEO_SMOOTH); if (!setting) return menu_cbs_exit(); - return menu_action_handle_setting(data, setting, + return menu_action_handle_setting(setting, setting_get_type(setting), MENU_ACTION_LEFT, wraparound); } @@ -743,8 +743,7 @@ static int bind_left_generic(void *data, unsigned type, const char *label, bool wraparound) { - return menu_setting_set(data, - type, label, MENU_ACTION_LEFT, wraparound); + return menu_setting_set(type, label, MENU_ACTION_LEFT, wraparound); } static int menu_cbs_init_bind_left_compare_label(menu_file_list_cbs_t *cbs, diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index d19808ae75..a689c3ced4 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -1761,7 +1761,7 @@ static int action_ok_lookup_setting(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return menu_setting_set(data, type, label, MENU_ACTION_OK, false); + return menu_setting_set(type, label, MENU_ACTION_OK, false); } static int action_ok_audio_add_to_mixer(void *data, diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index e79fcc6809..80572ebf28 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -312,7 +312,7 @@ static int action_right_shader_filter_default(void *data, rarch_setting_t *setting = menu_setting_find_enum(MENU_ENUM_LABEL_VIDEO_SMOOTH); if (!setting) return menu_cbs_exit(); - return menu_action_handle_setting(data, setting, + return menu_action_handle_setting(setting, setting_get_type(setting), MENU_ACTION_RIGHT, wraparound); } @@ -493,8 +493,7 @@ int bind_right_generic(void *data, unsigned type, const char *label, bool wraparound) { - return menu_setting_set(data, - type, label, MENU_ACTION_RIGHT, wraparound); + return menu_setting_set(type, label, MENU_ACTION_RIGHT, wraparound); } static int menu_cbs_init_bind_right_compare_type(menu_file_list_cbs_t *cbs, diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 6e5cf1d0b6..6f3e821e87 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -32,12 +32,11 @@ #ifndef BIND_ACTION_SELECT #define BIND_ACTION_SELECT(cbs, name) \ - cbs->action_select = name; \ + cbs->action_select = name; \ cbs->action_select_ident = #name; #endif -static int action_select_default(void *data, - const char *path, const char *label, unsigned type, +static int action_select_default(const char *path, const char *label, unsigned type, size_t idx) { menu_entry_t entry; @@ -45,7 +44,6 @@ static int action_select_default(void *data, enum menu_action action = MENU_ACTION_NOOP; menu_file_list_cbs_t *cbs = NULL; file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); - menu_handle_t *menu = (menu_handle_t*)data; menu_entry_init(&entry); menu_entry_get(&entry, 0, idx, NULL, false); @@ -97,7 +95,13 @@ static int action_select_default(void *data, } if (action != MENU_ACTION_NOOP) + { + menu_handle_t *menu = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + ret = menu_entry_action(&entry, menu, (unsigned)idx, action); + } menu_entry_free(&entry); @@ -106,76 +110,88 @@ static int action_select_default(void *data, return ret; } -static int action_select_path_use_directory( - void *data, - const char *path, +static int action_select_path_use_directory(const char *path, const char *label, unsigned type, size_t idx) { - return action_ok_path_use_directory((menu_handle_t*)data, + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return action_ok_path_use_directory(menu, path, label, type, idx, 0 /* unused */); } -static int action_select_driver_setting(void *data, - const char *path, +static int action_select_driver_setting(const char *path, const char *label, unsigned type, size_t idx) { - return bind_right_generic((menu_handle_t*)data, type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return bind_right_generic(menu, type, label, true); } -static int action_select_core_setting(void *data, - const char *path, +static int action_select_core_setting(const char *path, const char *label, unsigned type, size_t idx) { - return core_setting_right((menu_handle_t*)data, type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return core_setting_right(menu, type, label, true); } -static int shader_action_parameter_select(void *data, - const char *path, +static int shader_action_parameter_select(const char *path, const char *label, unsigned type, size_t idx) { - return shader_action_parameter_right((menu_handle_t*)data, type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return shader_action_parameter_right(menu, type, label, true); } -static int shader_action_parameter_preset_select( - void *data, const char *path, +static int shader_action_parameter_preset_select(const char *path, const char *label, unsigned type, size_t idx) { - return shader_action_parameter_right( - (menu_handle_t*)data, type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return shader_action_parameter_right(menu, type, label, true); } -static int action_select_cheat(void *data, - const char *path, +static int action_select_cheat(const char *path, const char *label, unsigned type, size_t idx) { - return action_right_cheat((menu_handle_t*)data, type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return action_right_cheat(menu, type, label, true); } -static int action_select_input_desc(void *data, - const char *path, +static int action_select_input_desc(const char *path, const char *label, unsigned type, size_t idx) { - return action_right_input_desc((menu_handle_t*)data, type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return action_right_input_desc(menu, type, label, true); } -static int action_select_input_desc_kbd(void *data, - const char *path, +static int action_select_input_desc_kbd(const char *path, const char *label, unsigned type, size_t idx) { - return action_right_input_desc_kbd((menu_handle_t*)data, type, label, true); + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + return action_right_input_desc_kbd(menu, type, label, true); } #ifdef HAVE_NETWORKING -static int action_select_netplay_connect_room( - void *data, - const char *path, +static int action_select_netplay_connect_room(const char *path, const char *label, unsigned type, size_t idx) { diff --git a/menu/cbs/menu_cbs_start.c b/menu/cbs/menu_cbs_start.c index b91093329e..399c49b490 100644 --- a/menu/cbs/menu_cbs_start.c +++ b/menu/cbs/menu_cbs_start.c @@ -45,19 +45,17 @@ #ifndef BIND_ACTION_START #define BIND_ACTION_START(cbs, name) \ - cbs->action_start = name; \ + cbs->action_start = name; \ cbs->action_start_ident = #name; #endif -static int action_start_remap_file_load(void *data, - unsigned type, const char *label) +static int action_start_remap_file_load(unsigned type, const char *label) { input_remapping_set_defaults(true); return 0; } -static int action_start_video_filter_file_load(void *data, - unsigned type, const char *label) +static int action_start_video_filter_file_load(unsigned type, const char *label) { settings_t *settings = config_get_ptr(); @@ -81,8 +79,7 @@ static int generic_action_start_performance_counters(struct retro_perf_counter * return 0; } -static int action_start_performance_counters_core(void *data, - unsigned type, const char *label) +static int action_start_performance_counters_core(unsigned type, const char *label) { struct retro_perf_counter **counters = retro_get_perf_counter_libretro(); unsigned offset = type - MENU_SETTINGS_LIBRETRO_PERF_COUNTERS_BEGIN; @@ -90,8 +87,7 @@ static int action_start_performance_counters_core(void *data, return generic_action_start_performance_counters(counters, offset, type, label); } -static int action_start_performance_counters_frontend( - void *data, unsigned type, +static int action_start_performance_counters_frontend(unsigned type, const char *label) { struct retro_perf_counter **counters = retro_get_perf_counter_rarch(); @@ -99,8 +95,7 @@ static int action_start_performance_counters_frontend( return generic_action_start_performance_counters(counters, offset, type, label); } -static int action_start_input_desc(void *data, - unsigned type, const char *label) +static int action_start_input_desc(unsigned type, const char *label) { settings_t *settings = config_get_ptr(); unsigned inp_desc_index_offset = type - MENU_SETTINGS_INPUT_DESC_BEGIN; @@ -123,12 +118,11 @@ static int action_start_input_desc(void *data, } static int action_start_shader_action_parameter( - void *data, unsigned type, const char *label) { video_shader_ctx_t shader_info; struct video_shader_parameter *param = NULL; - unsigned parameter = type - MENU_SETTINGS_SHADER_PARAMETER_0; + unsigned parameter = type - MENU_SETTINGS_SHADER_PARAMETER_0; video_shader_driver_get_current_shader(&shader_info); @@ -143,12 +137,11 @@ static int action_start_shader_action_parameter( return menu_shader_manager_clear_parameter(parameter); } -static int action_start_shader_pass(void *data, - unsigned type, const char *label) +static int action_start_shader_pass(unsigned type, const char *label) { - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); menu->hack_shader_pass = type - MENU_SETTINGS_SHADER_PASS_0; @@ -159,8 +152,7 @@ static int action_start_shader_pass(void *data, } -static int action_start_shader_scale_pass(void *data, - unsigned type, const char *label) +static int action_start_shader_scale_pass(unsigned type, const char *label) { unsigned pass = type - MENU_SETTINGS_SHADER_PASS_SCALE_0; @@ -169,39 +161,32 @@ static int action_start_shader_scale_pass(void *data, return 0; } -static int action_start_shader_filter_pass(void *data, - unsigned type, const char *label) +static int action_start_shader_filter_pass(unsigned type, const char *label) { unsigned pass = type - MENU_SETTINGS_SHADER_PASS_FILTER_0; return menu_shader_manager_clear_pass_filter(pass); } -static int action_start_netplay_mitm_server(void *data, - unsigned type, const char *label) +static int action_start_netplay_mitm_server(unsigned type, const char *label) { settings_t *settings = config_get_ptr(); - strlcpy(settings->arrays.netplay_mitm_server, - netplay_mitm_server, - sizeof(settings->arrays.netplay_mitm_server)); + strlcpy(settings->arrays.netplay_mitm_server, netplay_mitm_server, sizeof(settings->arrays.netplay_mitm_server)); return 0; } -static int action_start_shader_watch_for_changes(void *data, - unsigned type, const char *label) +static int action_start_shader_watch_for_changes(unsigned type, const char *label) { settings_t *settings = config_get_ptr(); settings->bools.video_shader_watch_files = video_shader_watch_files; return 0; } -static int action_start_shader_num_passes(void *data, - unsigned type, const char *label) +static int action_start_shader_num_passes(unsigned type, const char *label) { return menu_shader_manager_clear_num_passes(); } -static int action_start_cheat_num_passes(void *data, - unsigned type, const char *label) +static int action_start_cheat_num_passes(unsigned type, const char *label) { if (cheat_manager_get_size()) { @@ -213,8 +198,7 @@ static int action_start_cheat_num_passes(void *data, return 0; } -static int action_start_core_setting(void *data, - unsigned type, +static int action_start_core_setting(unsigned type, const char *label) { unsigned idx = type - MENU_SETTINGS_CORE_OPTION_START; @@ -226,8 +210,7 @@ static int action_start_core_setting(void *data, return 0; } -static int action_start_playlist_association(void *data, - unsigned type, const char *label) +static int action_start_playlist_association(unsigned type, const char *label) { int found; char new_playlist_cores[PATH_MAX_LENGTH]; @@ -262,8 +245,7 @@ static int action_start_playlist_association(void *data, return 0; } -static int action_start_video_resolution(void *data, - unsigned type, const char *label) +static int action_start_video_resolution(unsigned type, const char *label) { unsigned width = 0, height = 0; global_t *global = global_get_ptr(); @@ -286,9 +268,9 @@ static int action_start_video_resolution(void *data, return 0; } -static int action_start_lookup_setting(void *data, unsigned type, const char *label) +static int action_start_lookup_setting(unsigned type, const char *label) { - return menu_setting_set(data, type, label, MENU_ACTION_START, false); + return menu_setting_set(type, label, MENU_ACTION_START, false); } static int menu_cbs_init_bind_start_compare_label(menu_file_list_cbs_t *cbs) diff --git a/menu/menu_entries.h b/menu/menu_entries.h index 8d64746ff2..069c3ead24 100644 --- a/menu/menu_entries.h +++ b/menu/menu_entries.h @@ -103,8 +103,7 @@ typedef struct menu_file_list_cbs int (*action_iterate)(const char *label, unsigned action); int (*action_deferred_push)(menu_displaylist_info_t *info, void *data); - int (*action_select)(void *data, - const char *path, const char *label, unsigned type, + int (*action_select)(const char *path, const char *label, unsigned type, size_t idx); int (*action_get_title)(const char *path, const char *label, unsigned type, char *s, size_t len); @@ -115,8 +114,8 @@ typedef struct menu_file_list_cbs size_t idx); int (*action_scan)(const char *path, const char *label, unsigned type, size_t idx); - int (*action_start)(void *data, unsigned type, const char *label); - int (*action_info)(void *data, unsigned type, const char *label); + int (*action_start)(unsigned type, const char *label); + int (*action_info)(unsigned type, const char *label); int (*action_content_list_switch)(void *data, void *data2, void *userdata, const char *path, const char *label, unsigned type); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 335d75adc3..d1c46d6632 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -655,8 +655,7 @@ static int setting_handler(rarch_setting_t *setting, unsigned action) return -1; } -int menu_action_handle_setting(void *data, - rarch_setting_t *setting, +int menu_action_handle_setting(rarch_setting_t *setting, unsigned type, unsigned action, bool wraparound) { if (!setting) @@ -668,6 +667,7 @@ int menu_action_handle_setting(void *data, if (action == MENU_ACTION_OK) { menu_displaylist_info_t info; + menu_handle_t *menu = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); const char *name = setting->name; size_t selection = menu_navigation_get_selection(); @@ -680,8 +680,9 @@ int menu_action_handle_setting(void *data, info.directory_ptr = selection; info.list = menu_stack; - if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, - &info, (menu_handle_t*)data)) + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info, menu)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); @@ -813,8 +814,7 @@ int menu_setting_set_flags(rarch_setting_t *setting) return 0; } -int menu_setting_set(void *data, - unsigned type, const char *label, +int menu_setting_set(unsigned type, const char *label, unsigned action, bool wraparound) { int ret = 0; @@ -826,8 +826,7 @@ int menu_setting_set(void *data, if (!cbs) return 0; - ret = menu_action_handle_setting(data, - cbs->setting, + ret = menu_action_handle_setting(cbs->setting, type, action, wraparound); if (ret == -1) diff --git a/menu/menu_setting.h b/menu/menu_setting.h index a0e6a98bee..6fc32bdbfd 100644 --- a/menu/menu_setting.h +++ b/menu/menu_setting.h @@ -76,8 +76,7 @@ int menu_setting_generic(rarch_setting_t *setting, bool wraparound); int menu_setting_set_flags(rarch_setting_t *setting); -int menu_setting_set(void *data, - unsigned type, const char *label, +int menu_setting_set(unsigned type, const char *label, unsigned action, bool wraparound); /** @@ -121,12 +120,10 @@ void menu_setting_get_label(void *data, char *s, size_t len, unsigned *w, unsigned type, const char *menu_label, const char *label, unsigned idx); -int menu_action_handle_setting(void *data, - rarch_setting_t *setting, +int menu_action_handle_setting(rarch_setting_t *setting, unsigned type, unsigned action, bool wraparound); -enum setting_type menu_setting_get_browser_selection_type( - rarch_setting_t *setting); +enum setting_type menu_setting_get_browser_selection_type(rarch_setting_t *setting); void *setting_get_ptr(rarch_setting_t *setting); diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index e7f26d8f7f..85175600cd 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -462,7 +462,7 @@ int menu_entry_action(menu_entry_t *entry, break; case MENU_ACTION_START: if (cbs && cbs->action_start) - ret = cbs->action_start(data, entry->type, entry->label); + ret = cbs->action_start(entry->type, entry->label); break; case MENU_ACTION_LEFT: if (cbs && cbs->action_left) @@ -474,11 +474,11 @@ int menu_entry_action(menu_entry_t *entry, break; case MENU_ACTION_INFO: if (cbs && cbs->action_info) - ret = cbs->action_info(data, entry->type, entry->label); + ret = cbs->action_info(entry->type, entry->label); break; case MENU_ACTION_SELECT: if (cbs && cbs->action_select) - ret = cbs->action_select(data, entry->path, + ret = cbs->action_select(entry->path, entry->label, entry->type, i); break; case MENU_ACTION_SEARCH: From a2767108773ae8bbc93ecbbe6d26a356babe76c7 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:11:38 +0200 Subject: [PATCH 267/517] Revert "(Menu) More refactors" This reverts commit f0285ef7920fdcb6cdc8f8111600e9c66bb5df3d. --- menu/cbs/menu_cbs_contentlist_switch.c | 5 +- menu/cbs/menu_cbs_left.c | 80 +++++++++----------------- menu/cbs/menu_cbs_refresh.c | 6 +- menu/cbs/menu_cbs_right.c | 72 +++++++++-------------- menu/cbs/menu_cbs_select.c | 53 +++++------------ menu/drivers/materialui.c | 3 +- menu/menu_cbs.h | 19 +++--- menu/menu_displaylist.c | 8 ++- menu/menu_displaylist.h | 2 +- menu/menu_entries.h | 12 ++-- menu/widgets/menu_entry.c | 6 +- 11 files changed, 94 insertions(+), 172 deletions(-) diff --git a/menu/cbs/menu_cbs_contentlist_switch.c b/menu/cbs/menu_cbs_contentlist_switch.c index 001047b865..65d0af05ba 100644 --- a/menu/cbs/menu_cbs_contentlist_switch.c +++ b/menu/cbs/menu_cbs_contentlist_switch.c @@ -22,14 +22,13 @@ cbs->action_content_list_switch_ident = #name; #endif -static int deferred_push_content_list(void *data, - void *data2, void *userdata, +static int deferred_push_content_list(void *data, void *userdata, const char *path, const char *label, unsigned type) { file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); menu_navigation_set_selection(0); - return action_refresh_default(data, (file_list_t*)data2, selection_buf); + return action_refresh_default((file_list_t*)data, selection_buf); } int menu_cbs_init_bind_content_list_switch(menu_file_list_cbs_t *cbs, diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 322d10ff0b..04a403ca2e 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -309,8 +309,7 @@ static int generic_shader_action_parameter_left( return 0; } -static int shader_action_parameter_left(void *data, - unsigned type, const char *label, bool wraparound) +static int shader_action_parameter_left(unsigned type, const char *label, bool wraparound) { video_shader_ctx_t shader_info; struct video_shader *shader = menu_shader_get(); @@ -334,8 +333,7 @@ static int shader_action_parameter_left(void *data, return ret; } -static int action_left_cheat(void *data, - unsigned type, const char *label, +static int action_left_cheat(unsigned type, const char *label, bool wraparound) { size_t idx = type - MENU_SETTINGS_CHEAT_BEGIN; @@ -343,9 +341,8 @@ static int action_left_cheat(void *data, wraparound); } -static int action_left_input_desc(void *data, - unsigned type, const char *label, - bool wraparound) +static int action_left_input_desc(unsigned type, const char *label, + bool wraparound) { rarch_system_info_t *system = runloop_get_system_info(); settings_t *settings = config_get_ptr(); @@ -373,14 +370,13 @@ static int action_left_input_desc(void *data, also skip all the axes until analog remapping is implemented */ if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) /*|| (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)*/) - action_left_input_desc(data,type, label, wraparound); + action_left_input_desc(type, label, wraparound); return 0; } -static int action_left_input_desc_kbd(void *data, - unsigned type, const char *label, - bool wraparound) +static int action_left_input_desc_kbd(unsigned type, const char *label, + bool wraparound) { unsigned remap_id; unsigned key_id, id, offset; @@ -415,8 +411,7 @@ static int action_left_input_desc_kbd(void *data, return 0; } -static int action_left_scroll(void *data, - unsigned type, const char *label, +static int action_left_scroll(unsigned type, const char *label, bool wraparound) { size_t scroll_accel = 0; @@ -444,15 +439,14 @@ static int action_left_scroll(void *data, return 0; } -static int action_left_mainmenu(void *data, - unsigned type, const char *label, +static int action_left_mainmenu(unsigned type, const char *label, bool wraparound) { menu_ctx_list_t list_info; unsigned push_list = 0; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); menu_driver_ctl(RARCH_MENU_CTL_LIST_GET_SELECTION, &list_info); @@ -491,12 +485,12 @@ static int action_left_mainmenu(void *data, menu_driver_ctl(RARCH_MENU_CTL_LIST_CACHE, &list_info); if (cbs && cbs->action_content_list_switch) - return cbs->action_content_list_switch(menu, + return cbs->action_content_list_switch( selection_buf, menu_stack, "", "", 0); } break; case 2: - action_left_scroll(data, 0, "", false); + action_left_scroll(0, "", false); break; case 0: default: @@ -506,8 +500,7 @@ static int action_left_mainmenu(void *data, return 0; } -static int action_left_shader_scale_pass(void *data, - unsigned type, const char *label, +static int action_left_shader_scale_pass(unsigned type, const char *label, bool wraparound) { unsigned current_scale, delta; @@ -529,8 +522,7 @@ static int action_left_shader_scale_pass(void *data, return 0; } -static int action_left_shader_filter_pass(void *data, - unsigned type, const char *label, +static int action_left_shader_filter_pass(unsigned type, const char *label, bool wraparound) { unsigned delta = 2; @@ -545,8 +537,7 @@ static int action_left_shader_filter_pass(void *data, return 0; } -static int action_left_shader_filter_default(void *data, - unsigned type, const char *label, +static int action_left_shader_filter_default(unsigned type, const char *label, bool wraparound) { rarch_setting_t *setting = menu_setting_find_enum( @@ -557,8 +548,7 @@ static int action_left_shader_filter_default(void *data, setting_get_type(setting), MENU_ACTION_LEFT, wraparound); } -static int action_left_cheat_num_passes(void *data, - unsigned type, const char *label, +static int action_left_cheat_num_passes(unsigned type, const char *label, bool wraparound) { bool refresh = false; @@ -573,8 +563,7 @@ static int action_left_cheat_num_passes(void *data, return 0; } -static int action_left_shader_num_passes(void *data, - unsigned type, const char *label, +static int action_left_shader_num_passes(unsigned type, const char *label, bool wraparound) { bool refresh = false; @@ -594,8 +583,7 @@ static int action_left_shader_num_passes(void *data, return 0; } -static int action_left_netplay_mitm_server(void *data, - unsigned type, const char *label, +static int action_left_netplay_mitm_server(unsigned type, const char *label, bool wraparound) { settings_t *settings = config_get_ptr(); @@ -612,17 +600,13 @@ static int action_left_netplay_mitm_server(void *data, if (i - 1 >= 0) { found = true; - strlcpy(settings->arrays.netplay_mitm_server, - netplay_mitm_server_list[i - 1].name, - sizeof(settings->arrays.netplay_mitm_server)); + strlcpy(settings->arrays.netplay_mitm_server, netplay_mitm_server_list[i - 1].name, sizeof(settings->arrays.netplay_mitm_server)); break; } else if (wraparound) { found = true; - strlcpy(settings->arrays.netplay_mitm_server, - netplay_mitm_server_list[list_len - 1].name, - sizeof(settings->arrays.netplay_mitm_server)); + strlcpy(settings->arrays.netplay_mitm_server, netplay_mitm_server_list[list_len - 1].name, sizeof(settings->arrays.netplay_mitm_server)); break; } } @@ -631,16 +615,13 @@ static int action_left_netplay_mitm_server(void *data, if (!found) { /* current entry was invalid, go back to the end */ - strlcpy(settings->arrays.netplay_mitm_server, - netplay_mitm_server_list[list_len - 1].name, - sizeof(settings->arrays.netplay_mitm_server)); + strlcpy(settings->arrays.netplay_mitm_server, netplay_mitm_server_list[list_len - 1].name, sizeof(settings->arrays.netplay_mitm_server)); } return 0; } -static int action_left_shader_watch_for_changes(void *data, - unsigned type, const char *label, +static int action_left_shader_watch_for_changes(unsigned type, const char *label, bool wraparound) { settings_t *settings = config_get_ptr(); @@ -648,16 +629,14 @@ static int action_left_shader_watch_for_changes(void *data, return 0; } -static int action_left_video_resolution(void *data, - unsigned type, const char *label, +static int action_left_video_resolution(unsigned type, const char *label, bool wraparound) { video_driver_get_prev_video_out(); return 0; } -static int playlist_association_left(void *data, - unsigned type, const char *label, +static int playlist_association_left(unsigned type, const char *label, bool wraparound) { unsigned i; @@ -720,8 +699,7 @@ static int playlist_association_left(void *data, return 0; } -static int core_setting_left(void *data, - unsigned type, const char *label, +static int core_setting_left(unsigned type, const char *label, bool wraparound) { unsigned idx = type - MENU_SETTINGS_CORE_OPTION_START; @@ -731,16 +709,14 @@ static int core_setting_left(void *data, return 0; } -static int disk_options_disk_idx_left(void *data, - unsigned type, const char *label, +static int disk_options_disk_idx_left(unsigned type, const char *label, bool wraparound) { command_event(CMD_EVENT_DISK_PREV, NULL); return 0; } -static int bind_left_generic(void *data, - unsigned type, const char *label, +static int bind_left_generic(unsigned type, const char *label, bool wraparound) { return menu_setting_set(type, label, MENU_ACTION_LEFT, wraparound); diff --git a/menu/cbs/menu_cbs_refresh.c b/menu/cbs/menu_cbs_refresh.c index 87dba26f59..54881cfba6 100644 --- a/menu/cbs/menu_cbs_refresh.c +++ b/menu/cbs/menu_cbs_refresh.c @@ -22,18 +22,16 @@ cbs->action_refresh_ident = #name; #endif -int action_refresh_default(void *data, - file_list_t *list, file_list_t *menu_list) +int action_refresh_default(file_list_t *list, file_list_t *menu_list) { menu_displaylist_ctx_entry_t entry; - menu_handle_t *menu = (menu_handle_t*)data; if (!menu_list) return -1; entry.list = list; entry.stack = menu_list; - if (!menu_displaylist_push(&entry, menu)) + if (!menu_displaylist_push(&entry)) return -1; return 0; } diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 80572ebf28..0f2fa245a6 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -43,7 +43,7 @@ #ifndef BIND_ACTION_RIGHT #define BIND_ACTION_RIGHT(cbs, name) \ do { \ - cbs->action_right = name; \ + cbs->action_right = name; \ cbs->action_right_ident = #name; \ } while(0) #endif @@ -61,8 +61,7 @@ static int generic_shader_action_parameter_right(struct video_shader_parameter * return 0; } -int shader_action_parameter_right(void *data, - unsigned type, const char *label, bool wraparound) +int shader_action_parameter_right(unsigned type, const char *label, bool wraparound) { video_shader_ctx_t shader_info; struct video_shader *shader = menu_shader_get(); @@ -94,8 +93,7 @@ int generic_action_cheat_toggle(size_t idx, unsigned type, const char *label, return 0; } -int action_right_cheat(void *data, - unsigned type, const char *label, +int action_right_cheat(unsigned type, const char *label, bool wraparound) { size_t idx = type - MENU_SETTINGS_CHEAT_BEGIN; @@ -103,8 +101,7 @@ int action_right_cheat(void *data, wraparound); } -int action_right_input_desc_kbd(void *data, - unsigned type, const char *label, +int action_right_input_desc_kbd(unsigned type, const char *label, bool wraparound) { unsigned key_id, id, offset; @@ -143,8 +140,7 @@ int action_right_input_desc_kbd(void *data, } /* fix-me: incomplete, lacks error checking */ -int action_right_input_desc(void *data, - unsigned type, const char *label, +int action_right_input_desc(unsigned type, const char *label, bool wraparound) { rarch_system_info_t *system = runloop_get_system_info(); @@ -170,7 +166,7 @@ int action_right_input_desc(void *data, also skip all the axes until analog remapping is implemented */ if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) /*|| (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)*/) - action_right_input_desc(data, type, label, wraparound); + action_right_input_desc(type, label, wraparound); #if 0 int i = 0; @@ -182,8 +178,7 @@ int action_right_input_desc(void *data, return 0; } -static int action_right_scroll(void *data, - unsigned type, const char *label, +static int action_right_scroll(unsigned type, const char *label, bool wraparound) { size_t scroll_accel = 0; @@ -212,7 +207,7 @@ static int action_right_scroll(void *data, return 0; } -static int action_right_goto_tab(menu_handle_t *menu) +static int action_right_goto_tab(void) { menu_ctx_list_t list_info; file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); @@ -227,14 +222,13 @@ static int action_right_goto_tab(menu_handle_t *menu) menu_driver_ctl(RARCH_MENU_CTL_LIST_CACHE, &list_info); if (cbs && cbs->action_content_list_switch) - return cbs->action_content_list_switch(menu, selection_buf, menu_stack, + return cbs->action_content_list_switch(selection_buf, menu_stack, "", "", 0); return 0; } -static int action_right_mainmenu(void *data, - unsigned type, const char *label, +static int action_right_mainmenu(unsigned type, const char *label, bool wraparound) { menu_ctx_list_t list_info; @@ -259,16 +253,15 @@ static int action_right_mainmenu(void *data, if ((list_info.selection != (list_horiz_info.size + list_tabs_info.size)) || settings->bools.menu_navigation_wraparound_enable) - return action_right_goto_tab((menu_handle_t*)data); + return action_right_goto_tab(); } else - action_right_scroll(data, 0, "", false); + action_right_scroll(0, "", false); return 0; } -static int action_right_shader_scale_pass(void *data, - unsigned type, const char *label, +static int action_right_shader_scale_pass(unsigned type, const char *label, bool wraparound) { unsigned current_scale, delta; @@ -289,8 +282,7 @@ static int action_right_shader_scale_pass(void *data, return 0; } -static int action_right_shader_filter_pass(void *data, - unsigned type, const char *label, +static int action_right_shader_filter_pass(unsigned type, const char *label, bool wraparound) { unsigned pass = type - MENU_SETTINGS_SHADER_PASS_FILTER_0; @@ -305,8 +297,7 @@ static int action_right_shader_filter_pass(void *data, return 0; } -static int action_right_shader_filter_default(void *data, - unsigned type, const char *label, +static int action_right_shader_filter_default(unsigned type, const char *label, bool wraparound) { rarch_setting_t *setting = menu_setting_find_enum(MENU_ENUM_LABEL_VIDEO_SMOOTH); @@ -317,13 +308,13 @@ static int action_right_shader_filter_default(void *data, wraparound); } -static int action_right_cheat_num_passes(void *data, - unsigned type, const char *label, +static int action_right_cheat_num_passes(unsigned type, const char *label, bool wraparound) { bool refresh = false; - unsigned new_size = cheat_manager_get_size() + 1; + unsigned new_size = 0; + new_size = cheat_manager_get_size() + 1; menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); cheat_manager_realloc(new_size); @@ -331,8 +322,7 @@ static int action_right_cheat_num_passes(void *data, return 0; } -static int action_right_shader_num_passes(void *data, - unsigned type, const char *label, +static int action_right_shader_num_passes(unsigned type, const char *label, bool wraparound) { bool refresh = false; @@ -352,8 +342,7 @@ static int action_right_shader_num_passes(void *data, return 0; } -static int action_right_netplay_mitm_server(void *data, - unsigned type, const char *label, +static int action_right_netplay_mitm_server(unsigned type, const char *label, bool wraparound) { settings_t *settings = config_get_ptr(); @@ -391,26 +380,22 @@ static int action_right_netplay_mitm_server(void *data, return 0; } -static int action_right_shader_watch_for_changes(void *data, - unsigned type, const char *label, +static int action_right_shader_watch_for_changes(unsigned type, const char *label, bool wraparound) { settings_t *settings = config_get_ptr(); - settings->bools.video_shader_watch_files = - !settings->bools.video_shader_watch_files; + settings->bools.video_shader_watch_files = !settings->bools.video_shader_watch_files; return 0; } -static int action_right_video_resolution(void *data, - unsigned type, const char *label, +static int action_right_video_resolution(unsigned type, const char *label, bool wraparound) { video_driver_get_next_video_out(); return 0; } -static int playlist_association_right(void *data, - unsigned type, const char *label, +static int playlist_association_right(unsigned type, const char *label, bool wraparound) { char core_path[PATH_MAX_LENGTH]; @@ -469,8 +454,7 @@ static int playlist_association_right(void *data, return 0; } -int core_setting_right(void *data, - unsigned type, const char *label, +int core_setting_right(unsigned type, const char *label, bool wraparound) { unsigned idx = type - MENU_SETTINGS_CORE_OPTION_START; @@ -480,8 +464,7 @@ int core_setting_right(void *data, return 0; } -static int disk_options_disk_idx_right(void *data, - unsigned type, const char *label, +static int disk_options_disk_idx_right(unsigned type, const char *label, bool wraparound) { command_event(CMD_EVENT_DISK_NEXT, NULL); @@ -489,8 +472,7 @@ static int disk_options_disk_idx_right(void *data, return 0; } -int bind_right_generic(void *data, - unsigned type, const char *label, +int bind_right_generic(unsigned type, const char *label, bool wraparound) { return menu_setting_set(type, label, MENU_ACTION_RIGHT, wraparound); diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 6f3e821e87..8d31efe823 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -120,74 +120,47 @@ static int action_select_path_use_directory(const char *path, path, label, type, idx, 0 /* unused */); } -static int action_select_driver_setting(const char *path, - const char *label, unsigned type, +static int action_select_driver_setting(const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return bind_right_generic(menu, type, label, true); + return bind_right_generic(type, label, true); } -static int action_select_core_setting(const char *path, - const char *label, unsigned type, +static int action_select_core_setting(const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return core_setting_right(menu, type, label, true); + return core_setting_right(type, label, true); } -static int shader_action_parameter_select(const char *path, - const char *label, unsigned type, +static int shader_action_parameter_select(const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return shader_action_parameter_right(menu, type, label, true); + return shader_action_parameter_right(type, label, true); } -static int shader_action_parameter_preset_select(const char *path, - const char *label, unsigned type, +static int shader_action_parameter_preset_select(const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return shader_action_parameter_right(menu, type, label, true); + return shader_action_parameter_right(type, label, true); } -static int action_select_cheat(const char *path, - const char *label, unsigned type, +static int action_select_cheat(const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return action_right_cheat(menu, type, label, true); + return action_right_cheat(type, label, true); } -static int action_select_input_desc(const char *path, - const char *label, unsigned type, +static int action_select_input_desc(const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return action_right_input_desc(menu, type, label, true); + return action_right_input_desc(type, label, true); } static int action_select_input_desc_kbd(const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return action_right_input_desc_kbd(menu, type, label, true); + return action_right_input_desc_kbd(type, label, true); } #ifdef HAVE_NETWORKING diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index c4b040e231..1e0c9344eb 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -2354,8 +2354,7 @@ static int materialui_pointer_up(void *data, materialui_preswitch_tabs(mui, action); if (cbs && cbs->action_content_list_switch) - return cbs->action_content_list_switch(data, - selection_buf, menu_stack, + return cbs->action_content_list_switch(selection_buf, menu_stack, "", "", 0); } } diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 5083466d18..b5f14aa2c3 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -129,11 +129,9 @@ enum /* Function callbacks */ -int action_refresh_default(void *data, - file_list_t *list, file_list_t *menu_list); +int action_refresh_default(file_list_t *list, file_list_t *menu_list); -int shader_action_parameter_right(void *data, - unsigned type, const char *label, bool wraparound); +int shader_action_parameter_right(unsigned type, const char *label, bool wraparound); int generic_action_ok_displaylist_push(void *data, const char *path, const char *new_path, @@ -156,16 +154,16 @@ int generic_action_cheat_toggle(size_t idx, unsigned type, const char *label, bool wraparound); -int core_setting_right(void *data, unsigned type, const char *label, +int core_setting_right(unsigned type, const char *label, bool wraparound); -int action_right_input_desc(void *data, unsigned type, const char *label, +int action_right_input_desc(unsigned type, const char *label, bool wraparound); -int action_right_input_desc_kbd(void *data, unsigned type, const char *label, +int action_right_input_desc_kbd(unsigned type, const char *label, bool wraparound); -int action_right_cheat(void *data, unsigned type, const char *label, +int action_right_cheat(unsigned type, const char *label, bool wraparound); int setting_action_ok_video_refresh_rate_auto(void *data, bool wraparound); @@ -266,9 +264,8 @@ int action_scan_file(const char *path, const char *label, unsigned type, size_t idx); #endif -int bind_right_generic(void *data, - unsigned type, const char *label, - bool wraparound); +int bind_right_generic(unsigned type, const char *label, + bool wraparound); /* This sets up all the callback functions for a menu entry. * diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index af25053750..e192883774 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3904,7 +3904,7 @@ static bool menu_displaylist_push_internal( return false; } -bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry, void *data) +bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry) { menu_displaylist_info_t info; menu_file_list_cbs_t *cbs = NULL; @@ -3913,9 +3913,11 @@ bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry, void *data) unsigned type = 0; bool ret = false; enum msg_hash_enums enum_idx = MSG_UNKNOWN; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!entry || !menu) + if (!entry) + return false; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return false; menu_displaylist_info_init(&info); diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 45c66069f0..9614c11535 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -218,7 +218,7 @@ typedef struct menu_displaylist_ctx_entry bool menu_displaylist_process(menu_displaylist_info_t *info); -bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry, void *data); +bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry); void menu_displaylist_info_free(menu_displaylist_info_t *info); diff --git a/menu/menu_entries.h b/menu/menu_entries.h index 069c3ead24..57e5f1b604 100644 --- a/menu/menu_entries.h +++ b/menu/menu_entries.h @@ -116,15 +116,11 @@ typedef struct menu_file_list_cbs size_t idx); int (*action_start)(unsigned type, const char *label); int (*action_info)(unsigned type, const char *label); - int (*action_content_list_switch)(void *data, - void *data2, void *userdata, const char + int (*action_content_list_switch)(void *data, void *userdata, const char *path, const char *label, unsigned type); - int (*action_left)(void *data, - unsigned type, const char *label, bool wraparound); - int (*action_right)(void *data, - unsigned type, const char *label, bool wraparound); - int (*action_refresh)(void *data, - file_list_t *list, file_list_t *menu_list); + int (*action_left)(unsigned type, const char *label, bool wraparound); + int (*action_right)(unsigned type, const char *label, bool wraparound); + int (*action_refresh)(file_list_t *list, file_list_t *menu_list); int (*action_up)(unsigned type, const char *label); int (*action_label)(file_list_t *list, unsigned type, unsigned i, diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index 85175600cd..6e6aea9007 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -466,11 +466,11 @@ int menu_entry_action(menu_entry_t *entry, break; case MENU_ACTION_LEFT: if (cbs && cbs->action_left) - ret = cbs->action_left(data, entry->type, entry->label, false); + ret = cbs->action_left(entry->type, entry->label, false); break; case MENU_ACTION_RIGHT: if (cbs && cbs->action_right) - ret = cbs->action_right(data, entry->type, entry->label, false); + ret = cbs->action_right(entry->type, entry->label, false); break; case MENU_ACTION_INFO: if (cbs && cbs->action_info) @@ -504,7 +504,7 @@ int menu_entry_action(menu_entry_t *entry, bool refresh = false; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); - cbs->action_refresh(data, selection_buf, menu_stack); + cbs->action_refresh(selection_buf, menu_stack); menu_entries_ctl(MENU_ENTRIES_CTL_UNSET_REFRESH, &refresh); } } From fdffb7059dbd0847f1f3b6cab9067f9cd9433750 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:11:45 +0200 Subject: [PATCH 268/517] Revert "Less pointer grabbbing - reduce usage of RARCH_MENU_CTL_DRIVER_DATA_GET" This reverts commit fbf03df8be22b40a1d44908ebfe0163a0ebb4bef. --- menu/cbs/menu_cbs_deferred_push.c | 2 +- menu/cbs/menu_cbs_info.c | 5 +- menu/cbs/menu_cbs_ok.c | 2 +- menu/drivers/materialui.c | 36 ++++++----- menu/drivers/menu_generic.c | 2 +- menu/drivers/xmb.c | 57 ++++++++--------- menu/menu_displaylist.c | 88 ++++++++++++++++----------- menu/menu_displaylist.h | 3 +- menu/menu_driver.c | 3 +- menu/menu_driver.h | 2 +- menu/menu_setting.c | 10 +-- menu/widgets/menu_dialog.c | 7 +-- menu/widgets/menu_input_bind_dialog.c | 10 +-- 13 files changed, 109 insertions(+), 118 deletions(-) diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index df640bda49..d93cd79a62 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -53,7 +53,7 @@ enum static int deferred_push_dlist(menu_displaylist_info_t *info, void *data, enum menu_displaylist_ctl_state state) { - if (!menu_displaylist_ctl(state, info, data)) + if (!menu_displaylist_ctl(state, info)) return menu_cbs_exit(); menu_displaylist_process(info); return 0; diff --git a/menu/cbs/menu_cbs_info.c b/menu/cbs/menu_cbs_info.c index f6d673a212..453d5aeec6 100644 --- a/menu/cbs/menu_cbs_info.c +++ b/menu/cbs/menu_cbs_info.c @@ -33,7 +33,6 @@ static int action_info_default(unsigned type, const char *label) { menu_displaylist_info_t info; - menu_handle_t *menu = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); size_t selection = menu_navigation_get_selection(); @@ -45,9 +44,7 @@ static int action_info_default(unsigned type, const char *label) info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_INFO_SCREEN)); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (!menu_displaylist_ctl(DISPLAYLIST_HELP, &info, menu)) + if (!menu_displaylist_ctl(DISPLAYLIST_HELP, &info)) goto error; if (!menu_displaylist_process(&info)) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index a689c3ced4..c053fbb285 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -834,7 +834,7 @@ int generic_action_ok_displaylist_push( if (info_path) info.path = strdup(info_path); - if (menu_displaylist_ctl(dl_type, &info, menu)) + if (menu_displaylist_ctl(dl_type, &info)) { if (menu_displaylist_process(&info)) { diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 1e0c9344eb..f21a50afc2 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -1962,11 +1962,9 @@ static void materialui_populate_entries( } /* Context reset is called on launch or when a core is launched */ -static void materialui_context_reset(void *data, void *userdata, - bool is_threaded) +static void materialui_context_reset(void *data, bool is_threaded) { - menu_handle_t *menu = (menu_handle_t*)data; - materialui_handle_t *mui = (materialui_handle_t*)userdata; + materialui_handle_t *mui = (materialui_handle_t*)data; settings_t *settings = config_get_ptr(); if (!mui || !settings) @@ -2162,7 +2160,7 @@ static int materialui_list_push(void *data, void *userdata, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE))) { entry.enum_idx = MENU_ENUM_LABEL_CONTENT_SETTINGS; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } #ifndef HAVE_DYNAMIC @@ -2172,39 +2170,39 @@ static int materialui_list_push(void *data, void *userdata, if (settings->bools.menu_show_load_core) { entry.enum_idx = MENU_ENUM_LABEL_CORE_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } } if (system->load_no_content) { entry.enum_idx = MENU_ENUM_LABEL_START_CORE; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } if (settings->bools.menu_show_load_content) { entry.enum_idx = MENU_ENUM_LABEL_LOAD_CONTENT_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } if (settings->bools.menu_content_show_history) { entry.enum_idx = MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } #if defined(HAVE_NETWORKING) #ifdef HAVE_LAKKA entry.enum_idx = MENU_ENUM_LABEL_UPDATE_LAKKA; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); #else { settings_t *settings = config_get_ptr(); if (settings->bools.menu_show_online_updater) { entry.enum_idx = MENU_ENUM_LABEL_ONLINE_UPDATER; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } } #endif @@ -2212,42 +2210,42 @@ static int materialui_list_push(void *data, void *userdata, if (settings->bools.menu_content_show_netplay) { entry.enum_idx = MENU_ENUM_LABEL_NETPLAY; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } #endif if (settings->bools.menu_show_information) { entry.enum_idx = MENU_ENUM_LABEL_INFORMATION_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } #ifndef HAVE_DYNAMIC entry.enum_idx = MENU_ENUM_LABEL_RESTART_RETROARCH; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); #endif if (settings->bools.menu_show_configurations) { entry.enum_idx = MENU_ENUM_LABEL_CONFIGURATIONS_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } if (settings->bools.menu_show_help) { entry.enum_idx = MENU_ENUM_LABEL_HELP_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } #if !defined(IOS) entry.enum_idx = MENU_ENUM_LABEL_QUIT_RETROARCH; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); #endif #if defined(HAVE_LAKKA) if (settings->bools.menu_show_reboot) { entry.enum_idx = MENU_ENUM_LABEL_REBOOT; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } entry.enum_idx = MENU_ENUM_LABEL_SHUTDOWN; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); #endif info->need_push = true; ret = 0; diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 2424c888bd..4566b79c67 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -282,7 +282,7 @@ bool generic_menu_init_list(void *data) info.list = selection_buf; - if (menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info, data)) + if (menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index d9d77bb0d2..0391a439bb 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -1973,7 +1973,7 @@ static void xmb_context_destroy_horizontal_list(xmb_handle_t *xmb) } } -static void xmb_init_horizontal_list(menu_handle_t *menu, xmb_handle_t *xmb) +static void xmb_init_horizontal_list(xmb_handle_t *xmb) { menu_displaylist_info_t info; settings_t *settings = config_get_ptr(); @@ -1992,8 +1992,7 @@ static void xmb_init_horizontal_list(menu_handle_t *menu, xmb_handle_t *xmb) if (!string_is_empty(info.path)) { - if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL, - &info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL, &info)) { size_t i; for (i = 0; i < xmb->horizontal_list->size; i++) @@ -2032,7 +2031,6 @@ static void xmb_toggle_horizontal_list(xmb_handle_t *xmb) } static void xmb_context_reset_horizontal_list( - menu_handle_t *menu, xmb_handle_t *xmb) { unsigned i; @@ -2136,7 +2134,7 @@ static void xmb_context_reset_horizontal_list( xmb_toggle_horizontal_list(xmb); } -static void xmb_refresh_horizontal_list(menu_handle_t *menu, xmb_handle_t *xmb) +static void xmb_refresh_horizontal_list(xmb_handle_t *xmb) { xmb_context_destroy_horizontal_list(xmb); if (xmb->horizontal_list) @@ -2152,9 +2150,9 @@ static void xmb_refresh_horizontal_list(menu_handle_t *menu, xmb_handle_t *xmb) calloc(1, sizeof(file_list_t)); if (xmb->horizontal_list) - xmb_init_horizontal_list(menu, xmb); + xmb_init_horizontal_list(xmb); - xmb_context_reset_horizontal_list(menu, xmb); + xmb_context_reset_horizontal_list(xmb); } static int xmb_environ(enum menu_environ_cb type, void *data, void *userdata) @@ -2177,7 +2175,7 @@ static int xmb_environ(enum menu_environ_cb type, void *data, void *userdata) if (!xmb) return -1; - xmb_refresh_horizontal_list((menu_handle_t*)data, xmb); + xmb_refresh_horizontal_list(xmb); break; default: return -1; @@ -4101,7 +4099,7 @@ static void *xmb_init(void **userdata, bool video_is_threaded) xmb->horizontal_list = (file_list_t*)calloc(1, sizeof(file_list_t)); if (xmb->horizontal_list) - xmb_init_horizontal_list(menu, xmb); + xmb_init_horizontal_list(xmb); xmb_init_ribbon(xmb); @@ -4435,10 +4433,9 @@ static void xmb_context_reset_background(const char *iconpath) free(path); } -static void xmb_context_reset(void *data, void *userdata, bool is_threaded) +static void xmb_context_reset(void *data, bool is_threaded) { - menu_handle_t *menu = (menu_handle_t*)data; - xmb_handle_t *xmb = (xmb_handle_t*)userdata; + xmb_handle_t *xmb = (xmb_handle_t*)data; if (xmb) { @@ -4469,7 +4466,7 @@ static void xmb_context_reset(void *data, void *userdata, bool is_threaded) is_threaded); xmb_context_reset_textures(xmb, iconpath); xmb_context_reset_background(iconpath); - xmb_context_reset_horizontal_list(menu, xmb); + xmb_context_reset_horizontal_list(xmb); if (!string_is_equal(xmb_thumbnails_ident('R'), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) @@ -4836,7 +4833,7 @@ static int deferred_push_content_actions(menu_displaylist_info_t *info, void *data) { if (!menu_displaylist_ctl( - DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info, data)) + DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info)) return -1; menu_displaylist_process(info); menu_displaylist_info_free(info); @@ -4946,13 +4943,13 @@ static int xmb_list_push(void *data, void *userdata, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE))) { entry.enum_idx = MENU_ENUM_LABEL_CONTENT_SETTINGS; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } if (system->load_no_content) { entry.enum_idx = MENU_ENUM_LABEL_START_CORE; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } #ifndef HAVE_DYNAMIC @@ -4962,7 +4959,7 @@ static int xmb_list_push(void *data, void *userdata, if (settings->bools.menu_show_load_core) { entry.enum_idx = MENU_ENUM_LABEL_CORE_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } } @@ -4971,7 +4968,7 @@ static int xmb_list_push(void *data, void *userdata, const struct retro_subsystem_info* subsystem = NULL; entry.enum_idx = MENU_ENUM_LABEL_LOAD_CONTENT_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); subsystem = system->subsystem.data; @@ -5027,68 +5024,68 @@ static int xmb_list_push(void *data, void *userdata, } entry.enum_idx = MENU_ENUM_LABEL_ADD_CONTENT_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); #if defined(HAVE_NETWORKING) { settings_t *settings = config_get_ptr(); if (settings->bools.menu_show_online_updater && !settings->bools.kiosk_mode_enable) { entry.enum_idx = MENU_ENUM_LABEL_ONLINE_UPDATER; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } } #endif if (!settings->bools.menu_content_show_settings && !string_is_empty(settings->paths.menu_content_show_settings_password)) { entry.enum_idx = MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } if (settings->bools.kiosk_mode_enable && !string_is_empty(settings->paths.kiosk_mode_password)) { entry.enum_idx = MENU_ENUM_LABEL_MENU_DISABLE_KIOSK_MODE; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } if (settings->bools.menu_show_information) { entry.enum_idx = MENU_ENUM_LABEL_INFORMATION_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } #ifndef HAVE_DYNAMIC entry.enum_idx = MENU_ENUM_LABEL_RESTART_RETROARCH; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); #endif if (settings->bools.menu_show_configurations && !settings->bools.kiosk_mode_enable) { entry.enum_idx = MENU_ENUM_LABEL_CONFIGURATIONS_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } if (settings->bools.menu_show_help) { entry.enum_idx = MENU_ENUM_LABEL_HELP_LIST; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } #if !defined(IOS) if (settings->bools.menu_show_quit_retroarch) { entry.enum_idx = MENU_ENUM_LABEL_QUIT_RETROARCH; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } #endif if (settings->bools.menu_show_reboot) { entry.enum_idx = MENU_ENUM_LABEL_REBOOT; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); } entry.enum_idx = MENU_ENUM_LABEL_SHUTDOWN; - menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry, menu); + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); info->need_push = true; ret = 0; } @@ -5120,7 +5117,7 @@ static bool xmb_menu_init_list(void *data) info.list = selection_buf; - if (!menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info, data)) + if (!menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info)) goto error; info.need_push = true; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index e192883774..6b6c68251c 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -1585,8 +1585,7 @@ static enum msg_file_type extension_to_file_hash_type(const char *ext) return FILE_TYPE_NONE; } -static int menu_displaylist_parse_database_entry(menu_displaylist_info_t *info, - menu_handle_t *menu) +static int menu_displaylist_parse_database_entry(menu_displaylist_info_t *info) { unsigned i, j, k; char path_playlist[PATH_MAX_LENGTH]; @@ -1594,10 +1593,14 @@ static int menu_displaylist_parse_database_entry(menu_displaylist_info_t *info, char query[PATH_MAX_LENGTH]; playlist_t *playlist = NULL; database_info_list_t *db_info = NULL; + menu_handle_t *menu = NULL; settings_t *settings = config_get_ptr(); path_playlist[0] = path_base[0] = query[0] = '\0'; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + goto error; + database_info_build_query_enum(query, sizeof(query), DATABASE_QUERY_ENTRY, info->path_b); @@ -2497,13 +2500,13 @@ static void menu_displaylist_set_new_playlist( static int menu_displaylist_parse_horizontal_list( - menu_displaylist_info_t *info, - menu_handle_t *menu) + menu_displaylist_info_t *info) { menu_ctx_list_t list_info; menu_ctx_list_t list_horiz_info; bool is_historylist = false; playlist_t *playlist = NULL; + menu_handle_t *menu = NULL; struct item_file *item = NULL; settings_t *settings = config_get_ptr(); @@ -2519,6 +2522,9 @@ static int menu_displaylist_parse_horizontal_list( item = (struct item_file*)list_horiz_info.entry; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return -1; + if (!item) return -1; @@ -2556,10 +2562,14 @@ static int menu_displaylist_parse_horizontal_list( } static int menu_displaylist_parse_load_content_settings( - menu_displaylist_info_t *info, menu_handle_t *menu) + menu_displaylist_info_t *info) { + menu_handle_t *menu = NULL; settings_t *settings = config_get_ptr(); + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return -1; + if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) { #ifdef HAVE_LAKKA @@ -2760,18 +2770,24 @@ static int menu_displaylist_parse_load_content_settings( } static int menu_displaylist_parse_horizontal_content_actions( - menu_displaylist_info_t *info, menu_handle_t *menu) + menu_displaylist_info_t *info) { bool content_loaded = false; + unsigned idx = 0; + menu_handle_t *menu = NULL; const char *label = NULL; const char *entry_path = NULL; const char *core_path = NULL; const char *core_name = NULL; const char *db_name = NULL; + playlist_t *playlist = playlist_get_cached(); settings_t *settings = config_get_ptr(); const char *fullpath = path_get(RARCH_PATH_CONTENT); - unsigned idx = menu->rpl_entry_selection_ptr; - playlist_t *playlist = playlist_get_cached(); + + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return -1; + + idx = menu->rpl_entry_selection_ptr; if (playlist) playlist_get_index(playlist, idx, @@ -2781,7 +2797,7 @@ static int menu_displaylist_parse_horizontal_content_actions( && string_is_equal(menu->deferred_path, fullpath); if (content_loaded) - menu_displaylist_parse_load_content_settings(info, menu); + menu_displaylist_parse_load_content_settings(info); else { const char *ext = NULL; @@ -3186,12 +3202,16 @@ static int menu_displaylist_parse_options_cheats( } static int menu_displaylist_parse_options_remappings( - menu_displaylist_info_t *info, menu_handle_t *menu) + menu_displaylist_info_t *info) { unsigned p, retro_id; rarch_system_info_t *system = NULL; + menu_handle_t *menu = NULL; unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return -1; + for (p = 0; p < max_users; p++) { char key_type[PATH_MAX_LENGTH]; @@ -3759,24 +3779,21 @@ static void menu_displaylist_parse_playlist_associations( static bool menu_displaylist_push_internal( const char *label, menu_displaylist_ctx_entry_t *entry, - menu_displaylist_info_t *info, - void *data) + menu_displaylist_info_t *info) { - menu_handle_t *menu = (menu_handle_t*)data; - if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_SETTINGS_ALL, info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_SETTINGS_ALL, info)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB))) @@ -3795,7 +3812,7 @@ static bool menu_displaylist_push_internal( msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST)); menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - menu_displaylist_ctl(DISPLAYLIST_MUSIC_HISTORY, info, menu); + menu_displaylist_ctl(DISPLAYLIST_MUSIC_HISTORY, info); return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB))) @@ -3814,7 +3831,7 @@ static bool menu_displaylist_push_internal( msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST)); menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - menu_displaylist_ctl(DISPLAYLIST_VIDEO_HISTORY, info, menu); + menu_displaylist_ctl(DISPLAYLIST_VIDEO_HISTORY, info); return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB))) @@ -3844,7 +3861,7 @@ static bool menu_displaylist_push_internal( else info->need_push_no_playlist_entries = true; #endif - menu_displaylist_ctl(DISPLAYLIST_IMAGES_HISTORY, info, menu); + menu_displaylist_ctl(DISPLAYLIST_IMAGES_HISTORY, info); return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB))) @@ -3881,23 +3898,23 @@ static bool menu_displaylist_push_internal( info->path = strdup(settings->paths.directory_playlist); if (menu_displaylist_ctl( - DISPLAYLIST_DATABASE_PLAYLISTS, info, menu)) + DISPLAYLIST_DATABASE_PLAYLISTS, info)) return true; } } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_SCAN_DIRECTORY_LIST, info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_SCAN_DIRECTORY_LIST, info)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB))) { - if (menu_displaylist_ctl(DISPLAYLIST_NETPLAY_ROOM_LIST, info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_NETPLAY_ROOM_LIST, info)) return true; } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU))) { - if (menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL, info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL, info)) return true; } @@ -3938,7 +3955,7 @@ bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry) if (!info.list) goto error; - if (menu_displaylist_push_internal(label, entry, &info, menu)) + if (menu_displaylist_push_internal(label, entry, &info)) { ret = menu_displaylist_process(&info); goto end; @@ -4166,20 +4183,19 @@ void menu_displaylist_info_init(menu_displaylist_info_t *info) info->setting = NULL; } -bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data, - void *userdata) +bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) { size_t i; menu_ctx_displaylist_t disp_list; int ret = 0; core_info_list_t *list = NULL; + menu_handle_t *menu = NULL; bool load_content = true; bool use_filebrowser = false; menu_displaylist_info_t *info = (menu_displaylist_info_t*)data; - menu_handle_t *menu = (menu_handle_t*)userdata; settings_t *settings = config_get_ptr(); - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return false; core_info_get_list(&list); @@ -4257,7 +4273,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data, } #ifdef HAVE_LIBRETRODB - ret = menu_displaylist_parse_database_entry(info, menu); + ret = menu_displaylist_parse_database_entry(info); #else ret = 0; #endif @@ -4375,13 +4391,13 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data, menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); if (string_is_equal(info->path, file_path_str(FILE_PATH_CONTENT_HISTORY))) { - if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info)) return menu_displaylist_process(info); return false; } else if (string_is_equal(info->path, file_path_str(FILE_PATH_CONTENT_FAVORITES))) { - if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info)) return menu_displaylist_process(info); return false; } @@ -6143,7 +6159,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data, break; case DISPLAYLIST_HORIZONTAL: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - ret = menu_displaylist_parse_horizontal_list(info, menu); + ret = menu_displaylist_parse_horizontal_list(info); info->need_sort = true; info->need_refresh = true; @@ -6151,13 +6167,13 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data, break; case DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - ret = menu_displaylist_parse_horizontal_content_actions(info, menu); + ret = menu_displaylist_parse_horizontal_content_actions(info); info->need_refresh = true; info->need_push = true; break; case DISPLAYLIST_CONTENT_SETTINGS: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - ret = menu_displaylist_parse_load_content_settings(info, menu); + ret = menu_displaylist_parse_load_content_settings(info); info->need_refresh = true; info->need_push = true; @@ -6292,7 +6308,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data, break; case DISPLAYLIST_OPTIONS_REMAPPINGS: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - ret = menu_displaylist_parse_options_remappings(info, menu); + ret = menu_displaylist_parse_options_remappings(info); info->need_push = true; break; diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 9614c11535..7bcd105959 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -224,8 +224,7 @@ void menu_displaylist_info_free(menu_displaylist_info_t *info); void menu_displaylist_info_init(menu_displaylist_info_t *info); -bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data, - void *userdata); +bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data); #ifdef HAVE_NETWORKING void netplay_refresh_rooms_menu(file_list_t *list); #endif diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 3d25f3af27..9e8015c55b 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -1798,8 +1798,7 @@ static bool menu_driver_context_reset(bool video_is_threaded) { if (!menu_driver_ctx || !menu_driver_ctx->context_reset) return false; - menu_driver_ctx->context_reset(menu_driver_data, - menu_userdata, video_is_threaded); + menu_driver_ctx->context_reset(menu_userdata, video_is_threaded); return true; } diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 84c07a58d4..c9cbc48de9 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -443,7 +443,7 @@ typedef struct menu_ctx_driver * just toggled fullscreen, the GL driver did a teardown/setup - * we now need to rebuild all of our textures and state for the * menu driver. */ - void (*context_reset)(void *data, void *userdata, bool video_is_threaded); + void (*context_reset)(void *data, bool video_is_threaded); /* This will be invoked when we are running a hardware context * and the context in question wants to tear itself down. All * textures and related state on the menu driver will also diff --git a/menu/menu_setting.c b/menu/menu_setting.c index d1c46d6632..b785ed44b8 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -667,7 +667,6 @@ int menu_action_handle_setting(rarch_setting_t *setting, if (action == MENU_ACTION_OK) { menu_displaylist_info_t info; - menu_handle_t *menu = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); const char *name = setting->name; size_t selection = menu_navigation_get_selection(); @@ -680,9 +679,7 @@ int menu_action_handle_setting(rarch_setting_t *setting, info.directory_ptr = selection; info.list = menu_stack; - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); @@ -1375,7 +1372,6 @@ void general_write_handler(void *data) if (*setting->value.target.boolean) { menu_displaylist_info_t info; - menu_handle_t *menu = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); menu_displaylist_info_init(&info); @@ -1385,9 +1381,7 @@ void general_write_handler(void *data) msg_hash_to_str(MENU_ENUM_LABEL_HELP)); info.list = menu_stack; - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_GENERIC, &info)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); setting_set_with_string_representation(setting, "false"); diff --git a/menu/widgets/menu_dialog.c b/menu/widgets/menu_dialog.c index 23aebda618..4157e31502 100644 --- a/menu/widgets/menu_dialog.c +++ b/menu/widgets/menu_dialog.c @@ -255,8 +255,7 @@ void menu_dialog_push_pending(bool push, enum menu_dialog_type type) void menu_dialog_push(void) { menu_displaylist_info_t info; - const char *label = NULL; - menu_handle_t *menu = NULL; + const char *label; if (!menu_dialog_is_push_pending()) return; @@ -271,9 +270,7 @@ void menu_dialog_push(void) if (label) info.label = strdup(label); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - menu_displaylist_ctl(DISPLAYLIST_HELP, &info, menu); + menu_displaylist_ctl(DISPLAYLIST_HELP, &info); } void menu_dialog_set_current_id(unsigned id) diff --git a/menu/widgets/menu_input_bind_dialog.c b/menu/widgets/menu_input_bind_dialog.c index 2fc050aa01..55abf07785 100644 --- a/menu/widgets/menu_input_bind_dialog.c +++ b/menu/widgets/menu_input_bind_dialog.c @@ -86,7 +86,6 @@ static int menu_input_key_bind_set_mode_common( { menu_displaylist_info_t info; unsigned bind_type = 0; - menu_handle_t *menu = NULL; struct retro_keybind *keybind = NULL; unsigned index_offset = setting->index_offset; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); @@ -115,10 +114,7 @@ static int menu_input_key_bind_set_mode_common( info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND; info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND)); - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); break; @@ -134,9 +130,7 @@ static int menu_input_key_bind_set_mode_common( info.label = strdup( msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND_ALL)); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info, menu)) + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) menu_displaylist_process(&info); menu_displaylist_info_free(&info); break; From 6c99acb23e95b56e5e2b117373c95c9a2a05ece3 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:11:54 +0200 Subject: [PATCH 269/517] Revert "Pass around void pointer data - menu handle" This reverts commit b4a2bd5156354b084283165be96d43ee99bf50ec. --- menu/cbs/menu_cbs_deferred_push.c | 51 +++++++++++++------------------ menu/drivers/xmb.c | 3 +- menu/menu_displaylist.c | 7 ++--- menu/menu_entries.h | 2 +- 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index d93cd79a62..648caf0128 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -38,7 +38,7 @@ #ifndef BIND_ACTION_DEFERRED_PUSH #define BIND_ACTION_DEFERRED_PUSH(cbs, name) \ - cbs->action_deferred_push = name; \ + cbs->action_deferred_push = name; \ cbs->action_deferred_push_ident = #name; #endif @@ -50,8 +50,7 @@ enum PUSH_DETECT_CORE_LIST }; -static int deferred_push_dlist(menu_displaylist_info_t *info, - void *data, enum menu_displaylist_ctl_state state) +static int deferred_push_dlist(menu_displaylist_info_t *info, enum menu_displaylist_ctl_state state) { if (!menu_displaylist_ctl(state, info)) return menu_cbs_exit(); @@ -60,7 +59,7 @@ static int deferred_push_dlist(menu_displaylist_info_t *info, } static int deferred_push_database_manager_list_deferred( - menu_displaylist_info_t *info, void *data) + menu_displaylist_info_t *info) { if (!string_is_empty(info->path_b)) free(info->path_b); @@ -70,13 +69,13 @@ static int deferred_push_database_manager_list_deferred( info->path_b = strdup(info->path); info->path_c = NULL; - return deferred_push_dlist(info, data, DISPLAYLIST_DATABASE_QUERY); + return deferred_push_dlist(info, DISPLAYLIST_DATABASE_QUERY); } #define generic_deferred_push(name, type) \ -static int (name)(menu_displaylist_info_t *info, void *data) \ +static int (name)(menu_displaylist_info_t *info) \ { \ - return deferred_push_dlist(info, data, type); \ + return deferred_push_dlist(info, type); \ } generic_deferred_push(deferred_push_video_shader_preset_parameters, DISPLAYLIST_SHADER_PARAMETERS_PRESET) @@ -176,7 +175,7 @@ generic_deferred_push(deferred_push_lakka_list, DISPLAYLIST_ #endif static int deferred_push_cursor_manager_list_deferred( - menu_displaylist_info_t *info, void *data) + menu_displaylist_info_t *info) { char rdb_path[PATH_MAX_LENGTH]; int ret = -1; @@ -215,8 +214,7 @@ static int deferred_push_cursor_manager_list_deferred( info->path_c = strdup(query); info->path = strdup(rdb_path); - ret = deferred_push_dlist(info, data, - DISPLAYLIST_DATABASE_QUERY); + ret = deferred_push_dlist(info, DISPLAYLIST_DATABASE_QUERY); end: if (conf) @@ -229,8 +227,7 @@ end: #ifdef HAVE_LIBRETRODB static int deferred_push_cursor_manager_list_generic( - menu_displaylist_info_t *info, void *data, - enum database_query_type type) + menu_displaylist_info_t *info, enum database_query_type type) { char query[PATH_MAX_LENGTH]; int ret = -1; @@ -242,8 +239,7 @@ static int deferred_push_cursor_manager_list_generic( query[0] = '\0'; - database_info_build_query_enum(query, - sizeof(query), type, str_list->elems[0].data); + database_info_build_query_enum(query, sizeof(query), type, str_list->elems[0].data); if (string_is_empty(query)) goto end; @@ -259,8 +255,7 @@ static int deferred_push_cursor_manager_list_generic( info->path_b = strdup(str_list->elems[0].data); info->path_c = strdup(query); - ret = deferred_push_dlist(info, data, - DISPLAYLIST_DATABASE_QUERY); + ret = deferred_push_dlist(info, DISPLAYLIST_DATABASE_QUERY); end: string_list_free(str_list); @@ -268,9 +263,9 @@ end: } #define generic_deferred_cursor_manager(name, type) \ -static int (name)(menu_displaylist_info_t *info, void *data) \ +static int (name)(menu_displaylist_info_t *info) \ { \ - return deferred_push_cursor_manager_list_generic(info, data, type); \ + return deferred_push_cursor_manager_list_generic(info, type); \ } generic_deferred_cursor_manager(deferred_push_cursor_manager_list_deferred_query_rdb_entry_max_users, DATABASE_QUERY_ENTRY_MAX_USERS) @@ -294,7 +289,7 @@ generic_deferred_cursor_manager(deferred_push_cursor_manager_list_deferred_query #if 0 static int deferred_push_cursor_manager_list_deferred_query_subsearch( - menu_displaylist_info_t *info, void *data) + menu_displaylist_info_t *info) { int ret = -1; #ifdef HAVE_LIBRETRODB @@ -319,8 +314,7 @@ static int deferred_push_cursor_manager_list_deferred_query_subsearch( info->path_b = strdup(str_list->elems[0].data); info->path_c = strdup(query); - ret = deferred_push_dlist(info, data, - DISPLAYLIST_DATABASE_QUERY); + ret = deferred_push_dlist(info, DISPLAYLIST_DATABASE_QUERY); end: string_list_free(str_list); @@ -330,17 +324,16 @@ end: #endif static int general_push(menu_displaylist_info_t *info, - void *data, unsigned id, enum menu_displaylist_ctl_state state) { settings_t *settings = config_get_ptr(); char *newstring2 = NULL; core_info_list_t *list = NULL; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; rarch_system_info_t *system = runloop_get_system_info(); struct retro_system_info *system_menu = &system->info; - if (!data) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); core_info_get_list(&list); @@ -569,20 +562,20 @@ static int general_push(menu_displaylist_info_t *info, } free(newstring2); - return deferred_push_dlist(info, data, state); + return deferred_push_dlist(info, state); } #define generic_deferred_push_general(name, a, b) \ -static int (name)(menu_displaylist_info_t *info, void *data) \ +static int (name)(menu_displaylist_info_t *info) \ { \ - return general_push(info, data, a, b); \ + return general_push(info, a, b); \ } #define generic_deferred_push_clear_general(name, a, b) \ -static int (name)(menu_displaylist_info_t *info, void *data) \ +static int (name)(menu_displaylist_info_t *info) \ { \ menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); \ - return general_push(info, data, a, b); \ + return general_push(info, a, b); \ } generic_deferred_push_general(deferred_push_detect_core_list, PUSH_DETECT_CORE_LIST, DISPLAYLIST_CORES_DETECTED) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 0391a439bb..ce2c0ee83b 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -4829,8 +4829,7 @@ static void xmb_toggle(void *userdata, bool menu_on) xmb_toggle_horizontal_list(xmb); } -static int deferred_push_content_actions(menu_displaylist_info_t *info, - void *data) +static int deferred_push_content_actions(menu_displaylist_info_t *info) { if (!menu_displaylist_ctl( DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info)) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 6b6c68251c..e370a774de 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3930,12 +3930,9 @@ bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry) unsigned type = 0; bool ret = false; enum msg_hash_enums enum_idx = MSG_UNKNOWN; - menu_handle_t *menu = NULL; if (!entry) return false; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return false; menu_displaylist_info_init(&info); @@ -3964,8 +3961,10 @@ bool menu_displaylist_push(menu_displaylist_ctx_entry_t *entry) cbs = menu_entries_get_last_stack_actiondata(); if (cbs && cbs->action_deferred_push) - if (cbs->action_deferred_push(&info, menu) != 0) + { + if (cbs->action_deferred_push(&info) != 0) goto error; + } ret = true; diff --git a/menu/menu_entries.h b/menu/menu_entries.h index 57e5f1b604..3712dc4d1b 100644 --- a/menu/menu_entries.h +++ b/menu/menu_entries.h @@ -102,7 +102,7 @@ typedef struct menu_file_list_cbs rarch_setting_t *setting; int (*action_iterate)(const char *label, unsigned action); - int (*action_deferred_push)(menu_displaylist_info_t *info, void *data); + int (*action_deferred_push)(menu_displaylist_info_t *info); int (*action_select)(const char *path, const char *label, unsigned type, size_t idx); int (*action_get_title)(const char *path, const char *label, From 146c94a86a6aa7580dc15ca803ad0625e3e44340 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:12:01 +0200 Subject: [PATCH 270/517] Revert "(iOS) Buildfix" This reverts commit 15142fd0a98b0761c883366f817dd92326352ab9. --- ui/drivers/cocoa/cocoatouch_menu.m | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ui/drivers/cocoa/cocoatouch_menu.m b/ui/drivers/cocoa/cocoatouch_menu.m index 4ec02d1e1f..500591aee8 100644 --- a/ui/drivers/cocoa/cocoatouch_menu.m +++ b/ui/drivers/cocoa/cocoatouch_menu.m @@ -817,14 +817,11 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath - (void)menuBack { #ifdef HAVE_MENU - menu_handle_t *menu = NULL; - menu_entry_t entry = {0}; - size_t selection = menu_navigation_get_selection(); - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + menu_entry_t entry = {0}; + size_t selection = menu_navigation_get_selection(); menu_entry_get(&entry, 0, selection, NULL, false); - menu_entry_action(&entry, menu, (unsigned int)selection, MENU_ACTION_CANCEL); + menu_entry_action(&entry, (unsigned int)selection, MENU_ACTION_CANCEL); #endif } From b540ae3d4d8fb40e058a1ab72e112196b8a80fa3 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:12:08 +0200 Subject: [PATCH 271/517] Revert "Update" This reverts commit 21ede63df2ad2bd7d6d0f92b107d9202746f1c23. --- menu/cbs/menu_cbs_info.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/menu/cbs/menu_cbs_info.c b/menu/cbs/menu_cbs_info.c index 453d5aeec6..9694ddf26c 100644 --- a/menu/cbs/menu_cbs_info.c +++ b/menu/cbs/menu_cbs_info.c @@ -60,21 +60,17 @@ error: } #ifdef HAVE_CHEEVOS -int generic_action_ok_help(void *data, - const char *path, +int generic_action_ok_help(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums id, enum menu_dialog_type id2); static int action_info_cheevos(unsigned type, const char *label) { - menu_handle_t *menu = NULL; unsigned new_id = type - MENU_SETTINGS_CHEEVOS_START; menu_dialog_set_current_id(new_id); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - return generic_action_ok_help(menu, NULL, label, new_id, 0, 0, + return generic_action_ok_help(NULL, label, new_id, 0, 0, MENU_ENUM_LABEL_CHEEVOS_DESCRIPTION, MENU_DIALOG_HELP_CHEEVOS_DESCRIPTION); } From 1836d08b33f19e3785525bc28528d5f987cd1380 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:12:14 +0200 Subject: [PATCH 272/517] Revert "Start passing around menu_driver_data around properly" This reverts commit 7c314a91650c9b4d74222bbdefa37e014fa4ae0d. --- menu/cbs/menu_cbs_select.c | 8 +------- menu/drivers/materialui.c | 27 +++++++++++++++------------ menu/drivers/menu_generic.c | 2 +- menu/drivers/rgui.c | 7 ++----- menu/drivers/xmb.c | 7 +++---- menu/menu_driver.c | 11 +++-------- menu/menu_driver.h | 9 +++------ menu/menu_input.c | 30 ++++++------------------------ menu/widgets/menu_entry.c | 26 ++++++++++++-------------- menu/widgets/menu_entry.h | 3 +-- 10 files changed, 47 insertions(+), 83 deletions(-) diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 8d31efe823..15c8205bf7 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -95,13 +95,7 @@ static int action_select_default(const char *path, const char *label, unsigned t } if (action != MENU_ACTION_NOOP) - { - menu_handle_t *menu = NULL; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - ret = menu_entry_action(&entry, menu, (unsigned)idx, action); - } + ret = menu_entry_action(&entry, (unsigned)idx, action); menu_entry_free(&entry); diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index f21a50afc2..8d4e0deb7b 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -2268,8 +2268,7 @@ static size_t materialui_list_get_selection(void *data) /* The pointer or the mouse is pressed down. We use this callback to highlight the entry that has been pressed */ -static int materialui_pointer_down(void *data, - void *userdata, +static int materialui_pointer_down(void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) @@ -2277,16 +2276,22 @@ static int materialui_pointer_down(void *data, unsigned width, height; unsigned header_height; size_t entries_end = menu_entries_get_size(); - materialui_handle_t *mui = (materialui_handle_t*)userdata; + materialui_handle_t *mui = (materialui_handle_t*)userdata; if (!mui) return 0; - header_height = menu_display_get_header_height(); + header_height = menu_display_get_header_height(); video_driver_get_size(&width, &height); - if (y < header_height) { } - else if (y > height - mui->tabs_height) { } + if (y < header_height) + { + + } + else if (y > height - mui->tabs_height) + { + + } else if (ptr <= (entries_end - 1)) { size_t ii; @@ -2313,8 +2318,7 @@ static int materialui_pointer_down(void *data, If we clicked on the header, we perform a cancel action. If we clicked on the tabs, we switch to a new list. If we clicked on a menu entry, we call the entry action callback. */ -static int materialui_pointer_up(void *data, - void *userdata, +static int materialui_pointer_up(void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) @@ -2322,7 +2326,7 @@ static int materialui_pointer_up(void *data, unsigned width, height; unsigned header_height, i; size_t entries_end = menu_entries_get_size(); - materialui_handle_t *mui = (materialui_handle_t*)userdata; + materialui_handle_t *mui = (materialui_handle_t*)userdata; if (!mui) return 0; @@ -2333,7 +2337,7 @@ static int materialui_pointer_up(void *data, if (y < header_height) { size_t selection = menu_navigation_get_selection(); - return menu_entry_action(entry, data, (unsigned)selection, MENU_ACTION_CANCEL); + return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL); } else if (y > height - mui->tabs_height) { @@ -2372,8 +2376,7 @@ static int materialui_pointer_up(void *data, ) { if (ptr == ii && cbs && cbs->action_select) - return menu_entry_action(entry, data, - (unsigned)ii, MENU_ACTION_SELECT); + return menu_entry_action(entry, (unsigned)ii, MENU_ACTION_SELECT); } } } diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 4566b79c67..3661d5d5e2 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -229,7 +229,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) menu_entry_init(&entry); menu_entry_get(&entry, 0, selection, NULL, false); - ret = menu_entry_action(&entry, menu, + ret = menu_entry_action(&entry, (unsigned)selection, (enum menu_action)action); menu_entry_free(&entry); if (ret) diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 0c746d7cbd..16de9c2c43 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -868,7 +868,6 @@ static int rgui_environ(enum menu_environ_cb type, } static int rgui_pointer_tap(void *data, - void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) @@ -878,16 +877,14 @@ static int rgui_pointer_tap(void *data, if (y < header_height) { size_t selection = menu_navigation_get_selection(); - return menu_entry_action(entry, data, - (unsigned)selection, MENU_ACTION_CANCEL); + return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL); } else if (ptr <= (menu_entries_get_size() - 1)) { size_t selection = menu_navigation_get_selection(); if (ptr == selection && cbs && cbs->action_select) - return menu_entry_action(entry, data, - (unsigned)selection, MENU_ACTION_SELECT); + return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT); menu_navigation_set_selection(ptr); menu_driver_navigation_set(false); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index ce2c0ee83b..53ca07b3d7 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -5132,8 +5132,7 @@ error: return false; } -static int xmb_pointer_tap(void *data, - void *userdata, +static int xmb_pointer_tap(void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) @@ -5143,13 +5142,13 @@ static int xmb_pointer_tap(void *data, if (y < header_height) { size_t selection = menu_navigation_get_selection(); - return (unsigned)menu_entry_action(entry, data, (unsigned)selection, MENU_ACTION_CANCEL); + return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL); } else if (ptr <= (menu_entries_get_size() - 1)) { size_t selection = menu_navigation_get_selection(); if (ptr == selection && cbs && cbs->action_select) - return (unsigned)menu_entry_action(entry, data, (unsigned)selection, MENU_ACTION_SELECT); + return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT); menu_navigation_set_selection(ptr); menu_driver_navigation_set(false); diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 9e8015c55b..82b2932143 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -2119,8 +2119,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) point->retcode = 0; return false; } - point->retcode = menu_driver_ctx->pointer_tap(menu_driver_data, - menu_userdata, + point->retcode = menu_driver_ctx->pointer_tap(menu_userdata, point->x, point->y, point->ptr, point->cbs, point->entry, point->action); } @@ -2133,9 +2132,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) point->retcode = 0; return false; } - point->retcode = menu_driver_ctx->pointer_down( - menu_driver_data, - menu_userdata, + point->retcode = menu_driver_ctx->pointer_down(menu_userdata, point->x, point->y, point->ptr, point->cbs, point->entry, point->action); } @@ -2148,9 +2145,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) point->retcode = 0; return false; } - point->retcode = menu_driver_ctx->pointer_up( - menu_driver_data, - menu_userdata, + point->retcode = menu_driver_ctx->pointer_up(menu_userdata, point->x, point->y, point->ptr, point->cbs, point->entry, point->action); } diff --git a/menu/menu_driver.h b/menu/menu_driver.h index c9cbc48de9..412e920478 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -486,8 +486,7 @@ typedef struct menu_ctx_driver bool (*load_image)(void *userdata, void *data, enum menu_image_type type); const char *ident; int (*environ_cb)(enum menu_environ_cb type, void *data, void *userdata); - int (*pointer_tap)(void *data, void *userdata, - unsigned x, unsigned y, unsigned ptr, + int (*pointer_tap)(void *data, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action); void (*update_thumbnail_path)(void *data, unsigned i, char pos); @@ -497,12 +496,10 @@ typedef struct menu_ctx_driver int (*osk_ptr_at_pos)(void *data, int x, int y, unsigned width, unsigned height); void (*update_savestate_thumbnail_path)(void *data, unsigned i); void (*update_savestate_thumbnail_image)(void *data); - int (*pointer_down)(void *data, void *userdata, - unsigned x, unsigned y, unsigned ptr, + int (*pointer_down)(void *data, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action); - int (*pointer_up)(void *data, void *userdata, - unsigned x, unsigned y, unsigned ptr, + int (*pointer_up)(void *data, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action); } menu_ctx_driver_t; diff --git a/menu/menu_input.c b/menu/menu_input.c index a791ad83d3..8fdeb4f106 100644 --- a/menu/menu_input.c +++ b/menu/menu_input.c @@ -286,14 +286,8 @@ static int menu_input_mouse_frame( if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_BUTTON_R)) { - size_t selection = menu_navigation_get_selection(); - menu_handle_t *menu = NULL; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - menu_entry_action(entry, menu, - (unsigned)selection, - MENU_ACTION_CANCEL); + size_t selection = menu_navigation_get_selection(); + menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL); } if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_WHEEL_DOWN)) @@ -527,15 +521,9 @@ static int menu_input_pointer_post_iterate( { if (menu_input->pointer.counter > 32) { - size_t selection = menu_navigation_get_selection(); - menu_handle_t *menu = NULL; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - + size_t selection = menu_navigation_get_selection(); if (cbs && cbs->action_start) - return menu_entry_action(entry, menu, - (unsigned)selection, - MENU_ACTION_START); + return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_START); } else @@ -564,14 +552,8 @@ static int menu_input_pointer_post_iterate( { if (!pointer_oldback) { - menu_handle_t *menu = NULL; - pointer_oldback = true; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - menu_entry_action(entry, menu, - (unsigned)menu_navigation_get_selection(), - MENU_ACTION_CANCEL); + pointer_oldback = true; + menu_entry_action(entry, (unsigned)menu_navigation_get_selection(), MENU_ACTION_CANCEL); } } diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index 6e6aea9007..c0c28b8fdd 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -254,15 +254,12 @@ void menu_entry_pathdir_extensions(uint32_t i, char *s, size_t len) void menu_entry_reset(uint32_t i) { - menu_handle_t *menu = NULL; menu_entry_t entry; menu_entry_init(&entry); menu_entry_get(&entry, 0, i, NULL, true); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - menu_entry_action(&entry, menu, i, MENU_ACTION_START); + menu_entry_action(&entry, i, MENU_ACTION_START); } void menu_entry_get_value(menu_entry_t *entry, char *s, size_t len) @@ -411,21 +408,16 @@ bool menu_entry_is_currently_selected(unsigned id) int menu_entry_select(uint32_t i) { menu_entry_t entry; - menu_handle_t *menu = NULL; menu_navigation_set_selection(i); menu_entry_init(&entry); menu_entry_get(&entry, 0, i, NULL, false); - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - return menu_entry_action(&entry, menu, i, MENU_ACTION_SELECT); + return menu_entry_action(&entry, i, MENU_ACTION_SELECT); } -int menu_entry_action(menu_entry_t *entry, - void *data, - unsigned i, enum menu_action action) +int menu_entry_action(menu_entry_t *entry, unsigned i, enum menu_action action) { int ret = 0; file_list_t *selection_buf = @@ -456,9 +448,15 @@ int menu_entry_action(menu_entry_t *entry, break; case MENU_ACTION_OK: - if (cbs && cbs->action_ok) - ret = cbs->action_ok(data, entry->path, - entry->label, entry->type, i, entry->entry_idx); + { + menu_handle_t *menu = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); + + if (cbs && cbs->action_ok) + ret = cbs->action_ok(menu, entry->path, + entry->label, entry->type, i, entry->entry_idx); + } break; case MENU_ACTION_START: if (cbs && cbs->action_start) diff --git a/menu/widgets/menu_entry.h b/menu/widgets/menu_entry.h index 94ffb13f8b..b32ec148ee 100644 --- a/menu/widgets/menu_entry.h +++ b/menu/widgets/menu_entry.h @@ -113,8 +113,7 @@ void menu_entry_get(menu_entry_t *entry, size_t stack_idx, int menu_entry_select(uint32_t i); int menu_entry_action(menu_entry_t *entry, - void *data, - unsigned i, enum menu_action action); + unsigned i, enum menu_action action); void menu_entry_free(menu_entry_t *entry); From abad1b313461df715b756e50866612c77e3921cc Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:12:26 +0200 Subject: [PATCH 273/517] Revert "(menu) Start passing menu_handle to action_ok callback" This reverts commit d2a1c39f4b4e4aa874dcf21fa0f4272a7cf73662. --- menu/cbs/menu_cbs_ok.c | 455 ++++++++++++++++--------------------- menu/cbs/menu_cbs_select.c | 6 +- menu/menu_cbs.h | 23 +- menu/menu_driver.c | 3 +- menu/menu_entries.h | 3 +- menu/widgets/menu_entry.c | 12 +- 6 files changed, 205 insertions(+), 297 deletions(-) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index c053fbb285..c561763986 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -7,7 +7,7 @@ * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * @@ -96,7 +96,7 @@ enum #ifndef BIND_ACTION_OK #define BIND_ACTION_OK(cbs, name) \ do { \ - cbs->action_ok = name; \ + cbs->action_ok = name; \ cbs->action_ok_ident = #name; \ } while(0) #endif @@ -309,9 +309,7 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MSG_UNKNOWN; } -int generic_action_ok_displaylist_push( - void *data, - const char *path, +int generic_action_ok_displaylist_push(const char *path, const char *new_path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned action_type) @@ -325,7 +323,7 @@ int generic_action_ok_displaylist_push( const char *content_path = NULL; const char *info_label = NULL; const char *info_path = NULL; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; enum msg_hash_enums enum_idx = MSG_UNKNOWN; settings_t *settings = config_get_ptr(); file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); @@ -334,7 +332,7 @@ int generic_action_ok_displaylist_push( info.list = menu_stack; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) goto end; tmp[0] = '\0'; @@ -992,7 +990,6 @@ static void content_add_to_playlist(const char *path) } static int file_load_with_detect_core_wrapper( - void *data, enum msg_hash_enums enum_label_idx, enum msg_hash_enums enum_idx, size_t idx, size_t entry_idx, @@ -1004,10 +1001,10 @@ static int file_load_with_detect_core_wrapper( char *new_core_path = NULL; const char *menu_path = NULL; const char *menu_label = NULL; + menu_handle_t *menu = NULL; core_info_list_t *list = NULL; - menu_handle_t *menu = (menu_handle_t*)data; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); { @@ -1054,7 +1051,7 @@ static int file_load_with_detect_core_wrapper( if (enum_label_idx == MENU_ENUM_LABEL_COLLECTION) { free(new_core_path); - return generic_action_ok_displaylist_push(menu, path, NULL, + return generic_action_ok_displaylist_push(path, NULL, NULL, 0, idx, entry_idx, ACTION_OK_DL_DEFERRED_CORE_LIST_SET); } @@ -1084,8 +1081,7 @@ static int file_load_with_detect_core_wrapper( break; } case 0: - ret = generic_action_ok_displaylist_push(menu, - path, NULL, label, type, + ret = generic_action_ok_displaylist_push(path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_DEFERRED_CORE_LIST); break; default: @@ -1098,13 +1094,12 @@ static int file_load_with_detect_core_wrapper( } static int action_ok_file_load_with_detect_core_carchive( - void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); fill_pathname_join_delim(menu->detect_content_path, @@ -1114,14 +1109,12 @@ static int action_ok_file_load_with_detect_core_carchive( type = 0; label = NULL; - return file_load_with_detect_core_wrapper(menu, - MSG_UNKNOWN, + return file_load_with_detect_core_wrapper(MSG_UNKNOWN, MSG_UNKNOWN, idx, entry_idx, path, label, type, true); } -static int action_ok_file_load_with_detect_core(void *data, - const char *path, +static int action_ok_file_load_with_detect_core(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { @@ -1129,21 +1122,18 @@ static int action_ok_file_load_with_detect_core(void *data, label = NULL; return file_load_with_detect_core_wrapper( - data, MSG_UNKNOWN, MSG_UNKNOWN, idx, entry_idx, path, label, type, false); } -static int action_ok_file_load_with_detect_core_collection(void *data, - const char *path, +static int action_ok_file_load_with_detect_core_collection(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { type = 0; label = NULL; return file_load_with_detect_core_wrapper( - data, MENU_ENUM_LABEL_COLLECTION, MSG_UNKNOWN, idx, entry_idx, path, label, type, false); @@ -1170,8 +1160,7 @@ static int generic_action_ok_command(enum event_command cmd) return 0; } -static int generic_action_ok(void *data, - const char *path, +static int generic_action_ok(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned id, enum msg_hash_enums flush_id) { @@ -1182,9 +1171,9 @@ static int generic_action_ok(void *data, const char *menu_path = NULL; const char *menu_label = NULL; const char *flush_char = NULL; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) goto error; menu_entries_get_last_stack(&menu_path, @@ -1345,8 +1334,7 @@ error: return menu_cbs_exit(); } -static int default_action_ok_load_content_with_core_from_menu( - const char *_path, unsigned _type) +static int default_action_ok_load_content_with_core_from_menu(const char *_path, unsigned _type) { content_ctx_info_t content_info; content_info.argc = 0; @@ -1361,8 +1349,7 @@ static int default_action_ok_load_content_with_core_from_menu( return 0; } -static int default_action_ok_load_content_from_playlist_from_menu( - const char *_path, +static int default_action_ok_load_content_from_playlist_from_menu(const char *_path, const char *path, const char *entry_label) { content_ctx_info_t content_info; @@ -1380,9 +1367,9 @@ static int default_action_ok_load_content_from_playlist_from_menu( #define default_action_ok_set(funcname, _id, _flush) \ -static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return generic_action_ok(data, path, label, type, idx, entry_idx, _id, _flush); \ + return generic_action_ok(path, label, type, idx, entry_idx, _id, _flush); \ } default_action_ok_set(action_ok_set_path_audiofilter, ACTION_OK_SET_PATH_VIDEO_FILTER, MSG_UNKNOWN) @@ -1399,8 +1386,7 @@ default_action_ok_set(action_ok_remap_file_load, ACTION_OK_LOAD_REMAPPING_F default_action_ok_set(action_ok_shader_preset_load, ACTION_OK_LOAD_PRESET , MENU_ENUM_LABEL_SHADER_OPTIONS) default_action_ok_set(action_ok_shader_pass_load, ACTION_OK_LOAD_SHADER_PASS, MENU_ENUM_LABEL_SHADER_OPTIONS) -static int action_ok_file_load(void *data, - const char *path, +static int action_ok_file_load(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char menu_path_new[PATH_MAX_LENGTH]; @@ -1409,18 +1395,17 @@ static int action_ok_file_load(void *data, const char *menu_path = NULL; rarch_setting_t *setting = NULL; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); - menu_handle_t *menu = (menu_handle_t*)data; menu_path_new[0] = full_path_new[0] = '\0'; - if (!menu) - return menu_cbs_exit(); - if (filebrowser_get_type() == FILEBROWSER_SELECT_FILE_SUBSYSTEM) { /* TODO/FIXME - this path is triggered when we try to load a * file from an archive while inside the load subsystem * action */ + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); fill_pathname_join(menu_path_new, menu->scratch2_buf, menu->scratch_buf, @@ -1448,7 +1433,7 @@ static int action_ok_file_load(void *data, setting = menu_setting_find(menu_label); if (setting_get_type(setting) == ST_PATH) - return action_ok_set_path(data, path, label, type, idx, entry_idx); + return action_ok_set_path(path, label, type, idx, entry_idx); if (!string_is_empty(menu_path)) strlcpy(menu_path_new, menu_path, sizeof(menu_path_new)); @@ -1461,9 +1446,15 @@ static int action_ok_file_load(void *data, string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_ARCHIVE_OPEN)) ) + { + menu_handle_t *menu = NULL; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return menu_cbs_exit(); + fill_pathname_join(menu_path_new, menu->scratch2_buf, menu->scratch_buf, sizeof(menu_path_new)); + } } switch (type) @@ -1483,8 +1474,7 @@ static int action_ok_file_load(void *data, } -static int action_ok_playlist_entry_collection(void *data, - const char *path, +static int action_ok_playlist_entry_collection(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char new_core_path[PATH_MAX_LENGTH]; @@ -1496,9 +1486,9 @@ static int action_ok_playlist_entry_collection(void *data, const char *core_path = NULL; const char *core_name = NULL; playlist_t *tmp_playlist = NULL; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); new_core_path[0] = '\0'; @@ -1542,8 +1532,7 @@ static int action_ok_playlist_entry_collection(void *data, { /* TODO: figure out if this should refer to the inner or outer entry_path */ /* TODO: make sure there's only one entry_path in this function */ - int ret = action_ok_file_load_with_detect_core_collection(menu, - entry_path, + int ret = action_ok_file_load_with_detect_core_collection(entry_path, label, type, selection_ptr, entry_idx); if (playlist_initialized) playlist_free(tmp_playlist); @@ -1583,8 +1572,7 @@ static int action_ok_playlist_entry_collection(void *data, new_core_path, path, entry_label); } -static int action_ok_playlist_entry(void *data, - const char *path, +static int action_ok_playlist_entry(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char new_core_path[PATH_MAX_LENGTH]; @@ -1594,11 +1582,11 @@ static int action_ok_playlist_entry(void *data, const char *entry_label = NULL; const char *core_path = NULL; const char *core_name = NULL; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; new_core_path[0] = '\0'; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); selection_ptr = entry_idx; @@ -1626,8 +1614,7 @@ static int action_ok_playlist_entry(void *data, if (!found_associated_core) /* TODO: figure out if this should refer to the inner or outer entry_path */ /* TODO: make sure there's only one entry_path in this function */ - return action_ok_file_load_with_detect_core(menu, - entry_path, + return action_ok_file_load_with_detect_core(entry_path, label, type, selection_ptr, entry_idx); command_playlist_update_write(NULL, @@ -1659,8 +1646,7 @@ static int action_ok_playlist_entry(void *data, new_core_path, path, entry_label); } -static int action_ok_playlist_entry_start_content(void *data, - const char *path, +static int action_ok_playlist_entry_start_content(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { size_t selection_ptr = 0; @@ -1670,10 +1656,10 @@ static int action_ok_playlist_entry_start_content(void *data, const char *entry_label = NULL; const char *core_path = NULL; const char *core_name = NULL; + menu_handle_t *menu = NULL; playlist_t *tmp_playlist = playlist_get_cached(); - menu_handle_t *menu = (menu_handle_t*)data; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); if (!tmp_playlist) @@ -1718,8 +1704,7 @@ static int action_ok_playlist_entry_start_content(void *data, { /* TODO: figure out if this should refer to the inner or outer entry_path */ /* TODO: make sure there's only one entry_path in this function */ - int ret = action_ok_file_load_with_detect_core(menu, - entry_path, + int ret = action_ok_file_load_with_detect_core(entry_path, label, type, selection_ptr, entry_idx); if (playlist_initialized) playlist_free(tmp_playlist); @@ -1757,15 +1742,13 @@ error: return menu_cbs_exit(); } -static int action_ok_lookup_setting(void *data, - const char *path, +static int action_ok_lookup_setting(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return menu_setting_set(type, label, MENU_ACTION_OK, false); } -static int action_ok_audio_add_to_mixer(void *data, - const char *path, +static int action_ok_audio_add_to_mixer(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *entry_path = NULL; @@ -1784,16 +1767,15 @@ static int action_ok_audio_add_to_mixer(void *data, return 0; } -static int action_ok_audio_add_to_mixer_and_collection(void *data, - const char *path, +static int action_ok_audio_add_to_mixer_and_collection(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char combined_path[PATH_MAX_LENGTH]; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; combined_path[0] = '\0'; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); fill_pathname_join(combined_path, menu->scratch2_buf, @@ -1813,24 +1795,21 @@ static int action_ok_audio_add_to_mixer_and_collection(void *data, return 0; } -static int action_ok_menu_wallpaper(void *data, - const char *path, +static int action_ok_menu_wallpaper(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { filebrowser_set_type(FILEBROWSER_SELECT_IMAGE); - return action_ok_lookup_setting(data, path, label, type, idx, entry_idx); + return action_ok_lookup_setting(path, label, type, idx, entry_idx); } -static int action_ok_menu_font(void *data, - const char *path, +static int action_ok_menu_font(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { filebrowser_set_type(FILEBROWSER_SELECT_FONT); - return action_ok_lookup_setting(data, path, label, type, idx, entry_idx); + return action_ok_lookup_setting(path, label, type, idx, entry_idx); } -static int action_ok_menu_wallpaper_load(void *data, - const char *path, +static int action_ok_menu_wallpaper_load(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { settings_t *settings = config_get_ptr(); @@ -1838,18 +1817,17 @@ static int action_ok_menu_wallpaper_load(void *data, filebrowser_clear_type(); settings->uints.menu_xmb_shader_pipeline = XMB_SHADER_PIPELINE_WALLPAPER; - return generic_action_ok(data, path, label, type, idx, entry_idx, + return generic_action_ok(path, label, type, idx, entry_idx, ACTION_OK_LOAD_WALLPAPER, MSG_UNKNOWN); } -int generic_action_ok_help(void *data, - const char *path, +int generic_action_ok_help(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums id, enum menu_dialog_type id2) { const char *lbl = msg_hash_to_str(id); - return generic_action_ok_displaylist_push(data, path, NULL, lbl, id2, idx, + return generic_action_ok_displaylist_push(path, NULL, lbl, id2, idx, entry_idx, ACTION_OK_DL_HELP); } @@ -2012,7 +1990,7 @@ static void menu_input_st_string_cb_cheat_file_save_as( } #define default_action_dialog_start(funcname, _label_setting, _idx, _cb) \ -static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ menu_input_ctx_line_t line; \ line.label = label; \ @@ -2061,8 +2039,7 @@ enum ACTION_OK_SHADER_PRESET_SAVE_PARENT }; -static int generic_action_ok_shader_preset_save(void *data, - const char *path, +static int generic_action_ok_shader_preset_save(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned action_type) { @@ -2126,32 +2103,28 @@ static int generic_action_ok_shader_preset_save(void *data, return 0; } -static int action_ok_shader_preset_save_core(void *data, - const char *path, +static int action_ok_shader_preset_save_core(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_shader_preset_save(data, path, label, type, + return generic_action_ok_shader_preset_save(path, label, type, idx, entry_idx, ACTION_OK_SHADER_PRESET_SAVE_CORE); } -static int action_ok_shader_preset_save_game(void *data, - const char *path, +static int action_ok_shader_preset_save_game(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_shader_preset_save(data, path, label, type, + return generic_action_ok_shader_preset_save(path, label, type, idx, entry_idx, ACTION_OK_SHADER_PRESET_SAVE_GAME); } -static int action_ok_shader_preset_save_parent(void *data, - const char *path, +static int action_ok_shader_preset_save_parent(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_shader_preset_save(data, path, label, type, + return generic_action_ok_shader_preset_save(path, label, type, idx, entry_idx, ACTION_OK_SHADER_PRESET_SAVE_PARENT); } -static int generic_action_ok_remap_file_operation(void *data, - const char *path, +static int generic_action_ok_remap_file_operation(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned action_type) { @@ -2239,76 +2212,68 @@ static int generic_action_ok_remap_file_operation(void *data, return 0; } -static int action_ok_remap_file_save_core(void *data, - const char *path, +static int action_ok_remap_file_save_core(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_remap_file_operation(data, path, label, type, + return generic_action_ok_remap_file_operation(path, label, type, idx, entry_idx, ACTION_OK_REMAP_FILE_SAVE_CORE); } -static int action_ok_remap_file_save_game(void *data, - const char *path, +static int action_ok_remap_file_save_game(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_remap_file_operation(data, path, label, type, + return generic_action_ok_remap_file_operation(path, label, type, idx, entry_idx, ACTION_OK_REMAP_FILE_SAVE_GAME); } -static int action_ok_remap_file_remove_core(void *data, - const char *path, +static int action_ok_remap_file_remove_core(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_remap_file_operation(data, path, label, type, + return generic_action_ok_remap_file_operation(path, label, type, idx, entry_idx, ACTION_OK_REMAP_FILE_REMOVE_CORE); } -static int action_ok_remap_file_remove_game(void *data, - const char *path, +static int action_ok_remap_file_remove_game(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_remap_file_operation(data, path, label, type, + return generic_action_ok_remap_file_operation(path, label, type, idx, entry_idx, ACTION_OK_REMAP_FILE_REMOVE_GAME); } -int action_ok_path_use_directory(void *data, - const char *path, +int action_ok_path_use_directory(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { filebrowser_clear_type(); - return generic_action_ok(data, NULL, label, type, idx, entry_idx, + return generic_action_ok(NULL, label, type, idx, entry_idx, ACTION_OK_SET_DIRECTORY, MSG_UNKNOWN); } #ifdef HAVE_LIBRETRODB -static int action_ok_scan_file(void *data, - const char *path, +static int action_ok_scan_file(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return action_scan_file(path, label, type, idx); } -static int action_ok_path_scan_directory(void *data, - const char *path, +static int action_ok_path_scan_directory(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return action_scan_directory(NULL, label, type, idx); } #endif -static int action_ok_core_deferred_set(void *data, - const char *new_core_path, +static int action_ok_core_deferred_set(const char *new_core_path, const char *content_label, unsigned type, size_t idx, size_t entry_idx) { char ext_name[255]; char core_display_name[PATH_MAX_LENGTH]; settings_t *settings = config_get_ptr(); + menu_handle_t *menu = NULL; size_t selection = menu_navigation_get_selection(); - menu_handle_t *menu = (menu_handle_t*)data; ext_name[0] = '\0'; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); if (!frontend_driver_get_core_extension(ext_name, sizeof(ext_name))) @@ -2338,26 +2303,24 @@ static int action_ok_core_deferred_set(void *data, return menu_cbs_exit(); } -static int action_ok_deferred_list_stub(void *data, - const char *path, +static int action_ok_deferred_list_stub(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return 0; } -static int action_ok_load_core_deferred(void *data, - const char *path, +static int action_ok_load_core_deferred(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { content_ctx_info_t content_info; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; content_info.argc = 0; content_info.argv = NULL; content_info.args = NULL; content_info.environ_get = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); if (!task_push_load_content_with_new_core_from_menu( @@ -2372,7 +2335,7 @@ static int action_ok_load_core_deferred(void *data, } #define default_action_ok_start_builtin_core(funcname, _id) \ -static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ content_ctx_info_t content_info; \ content_info.argc = 0; \ @@ -2388,7 +2351,7 @@ default_action_ok_start_builtin_core(action_ok_start_net_retropad_core, CORE_TYP default_action_ok_start_builtin_core(action_ok_start_video_processor_core, CORE_TYPE_VIDEO_PROCESSOR) #ifdef HAVE_FFMPEG -static int action_ok_file_load_ffmpeg(void *data, const char *path, +static int action_ok_file_load_ffmpeg(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char new_path[PATH_MAX_LENGTH]; @@ -2407,15 +2370,15 @@ static int action_ok_file_load_ffmpeg(void *data, const char *path, } #endif -static int action_ok_audio_run(void *data, const char *path, +static int action_ok_audio_run(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char combined_path[PATH_MAX_LENGTH]; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; combined_path[0] = '\0'; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); fill_pathname_join(combined_path, menu->scratch2_buf, @@ -2424,8 +2387,7 @@ static int action_ok_audio_run(void *data, const char *path, return default_action_ok_load_content_with_core_from_menu(combined_path, CORE_TYPE_FFMPEG); } -static int action_ok_file_load_imageviewer(void *data, - const char *path, +static int action_ok_file_load_imageviewer(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char fullpath[PATH_MAX_LENGTH]; @@ -2443,27 +2405,25 @@ static int action_ok_file_load_imageviewer(void *data, return default_action_ok_load_content_with_core_from_menu(fullpath, CORE_TYPE_IMAGEVIEWER); } -static int action_ok_file_load_current_core(void *data, - const char *path, +static int action_ok_file_load_current_core(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!data) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); return default_action_ok_load_content_with_core_from_menu( menu->detect_content_path, CORE_TYPE_PLAIN); } -static int action_ok_file_load_detect_core(void *data, - const char *path, +static int action_ok_file_load_detect_core(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { content_ctx_info_t content_info; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); content_info.argc = 0; @@ -2482,7 +2442,8 @@ static int action_ok_file_load_detect_core(void *data, return 0; } -static int action_ok_load_state(void *data, const char *path, + +static int action_ok_load_state(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { if (generic_action_ok_command(CMD_EVENT_LOAD_STATE) == -1) @@ -2490,8 +2451,7 @@ static int action_ok_load_state(void *data, const char *path, return generic_action_ok_command(CMD_EVENT_RESUME); } -static int action_ok_save_state(void *data, - const char *path, +static int action_ok_save_state(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { if (generic_action_ok_command(CMD_EVENT_SAVE_STATE) == -1) @@ -2499,8 +2459,7 @@ static int action_ok_save_state(void *data, return generic_action_ok_command(CMD_EVENT_RESUME); } -static int action_ok_undo_load_state(void *data, - const char *path, +static int action_ok_undo_load_state(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { if (generic_action_ok_command(CMD_EVENT_UNDO_LOAD_STATE) == -1) @@ -2508,8 +2467,7 @@ static int action_ok_undo_load_state(void *data, return generic_action_ok_command(CMD_EVENT_RESUME); } -static int action_ok_undo_save_state(void *data, - const char *path, +static int action_ok_undo_save_state(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { if (generic_action_ok_command(CMD_EVENT_UNDO_SAVE_STATE) == -1) @@ -2553,9 +2511,7 @@ static void cb_decompressed(void *task_data, void *user_data, const char *err) } #endif -static int generic_action_ok_network( - void *data, - const char *path, +static int generic_action_ok_network(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums enum_idx) { @@ -2639,14 +2595,14 @@ static int generic_action_ok_network( task_push_http_transfer(url_path, suppress_msg, url_label, callback, transf); - return generic_action_ok_displaylist_push(data, path, NULL, + return generic_action_ok_displaylist_push(path, NULL, label, type, idx, entry_idx, type_id2); } #define default_action_ok_list(funcname, _id) \ -static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return generic_action_ok_network(data, path, label, type, idx, entry_idx, _id); \ + return generic_action_ok_network(path, label, type, idx, entry_idx, _id); \ } default_action_ok_list(action_ok_core_content_list, MENU_ENUM_LABEL_CB_CORE_CONTENT_LIST) @@ -2662,12 +2618,8 @@ static void cb_generic_dir_download(void *task_data, if (transf) { - menu_handle_t *menu = NULL; - - if (menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - generic_action_ok_network(menu, - transf->path, transf->path, 0, 0, 0, - MENU_ENUM_LABEL_CB_CORE_CONTENT_LIST); + generic_action_ok_network(transf->path, transf->path, 0, 0, 0, + MENU_ENUM_LABEL_CB_CORE_CONTENT_LIST); free(transf); } @@ -2840,8 +2792,7 @@ finish: #endif -static int action_ok_download_generic(void *data, - const char *path, +static int action_ok_download_generic(const char *path, const char *label, const char *menu_label, unsigned type, size_t idx, size_t entry_idx, enum msg_hash_enums enum_idx) @@ -2932,8 +2883,7 @@ static int action_ok_download_generic(void *data, return 0; } -static int action_ok_core_content_download(void *data, - const char *path, +static int action_ok_core_content_download(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *menu_path = NULL; @@ -2942,15 +2892,15 @@ static int action_ok_core_content_download(void *data, menu_entries_get_last_stack(&menu_path, &menu_label, NULL, &enum_idx, NULL); - return action_ok_download_generic(data, path, label, + return action_ok_download_generic(path, label, menu_path, type, idx, entry_idx, MENU_ENUM_LABEL_CB_CORE_CONTENT_DOWNLOAD); } #define default_action_ok_download(funcname, _id) \ -static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return action_ok_download_generic(data, path, label, NULL, type, idx, entry_idx,_id); \ + return action_ok_download_generic(path, label, NULL, type, idx, entry_idx,_id); \ } default_action_ok_download(action_ok_core_content_thumbnails, MENU_ENUM_LABEL_CB_CORE_THUMBNAILS_DOWNLOAD) @@ -2969,8 +2919,7 @@ default_action_ok_download(action_ok_update_cheats, MENU_ENUM_LABEL_CB_UPDATE_CH default_action_ok_download(action_ok_update_autoconfig_profiles, MENU_ENUM_LABEL_CB_UPDATE_AUTOCONFIG_PROFILES) /* creates folder and core options stub file for subsequent runs */ -static int action_ok_option_create(void *data, - const char *path, +static int action_ok_option_create(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char game_path[PATH_MAX_LENGTH]; @@ -3007,9 +2956,7 @@ static int action_ok_option_create(void *data, return 0; } -int action_ok_close_content(void *data, - const char *path, const char *label, - unsigned type, size_t idx, size_t entry_idx) +int action_ok_close_content(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { /* This line resets the navigation pointer so the active entry will be "Run" */ menu_navigation_set_selection(0); @@ -3017,7 +2964,7 @@ int action_ok_close_content(void *data, } #define default_action_ok_cmd_func(func_name, cmd) \ -int (func_name)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +int (func_name)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ return generic_action_ok_command(cmd); \ } @@ -3032,17 +2979,16 @@ default_action_ok_cmd_func(action_ok_disk_cycle_tray_status, CMD_EVENT_DISK_EJEC default_action_ok_cmd_func(action_ok_shader_apply_changes, CMD_EVENT_SHADERS_APPLY_CHANGES ) -static int action_ok_reset_core_association(void *data, - const char *path, +static int action_ok_reset_core_association(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *tmp_path = NULL; + menu_handle_t *menu = NULL; playlist_t *tmp_playlist = playlist_get_cached(); - menu_handle_t *menu = (menu_handle_t*)data; if (!tmp_playlist) return 0; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); playlist_get_index(tmp_playlist, @@ -3055,8 +3001,7 @@ static int action_ok_reset_core_association(void *data, return 0; } -static int action_ok_add_to_favorites(void *data, - const char *path, +static int action_ok_add_to_favorites(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { void *new_path = (void*)path_get(RARCH_PATH_CONTENT); @@ -3065,17 +3010,16 @@ static int action_ok_add_to_favorites(void *data, return 0; } -static int action_ok_add_to_favorites_playlist(void *data, - const char *path, +static int action_ok_add_to_favorites_playlist(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *tmp_path = NULL; + menu_handle_t *menu = NULL; playlist_t *tmp_playlist = playlist_get_cached(); - menu_handle_t *menu = (menu_handle_t*)data; if (!tmp_playlist) return 0; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); playlist_get_index(tmp_playlist, @@ -3088,8 +3032,7 @@ static int action_ok_add_to_favorites_playlist(void *data, } -static int action_ok_delete_entry(void *data, - const char *path, +static int action_ok_delete_entry(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { size_t new_selection_ptr; @@ -3102,10 +3045,10 @@ static int action_ok_delete_entry(void *data, #ifdef HAVE_IMAGEVIEWER char *def_conf_img_path = NULL; #endif + menu_handle_t *menu = NULL; playlist_t *playlist = playlist_get_cached(); - menu_handle_t *menu = (menu_handle_t*)data; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); conf_path = playlist_get_conf_path(playlist); @@ -3145,8 +3088,7 @@ static int action_ok_delete_entry(void *data, } -static int action_ok_rdb_entry_submenu(void *data, - const char *path, +static int action_ok_rdb_entry_submenu(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { union string_list_elem_attr attr; @@ -3198,7 +3140,7 @@ static int action_ok_rdb_entry_submenu(void *data, str_list->elems[0].data, '_', sizeof(new_label)); - ret = generic_action_ok_displaylist_push(data, new_path, NULL, + ret = generic_action_ok_displaylist_push(new_path, NULL, new_label, type, idx, entry_idx, ACTION_OK_DL_RDB_ENTRY_SUBMENU); @@ -3214,9 +3156,9 @@ end: } #define default_action_ok_func(func_name, lbl) \ -int (func_name)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +int (func_name)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, entry_idx, lbl); \ + return generic_action_ok_displaylist_push(path, NULL, label, type, idx, entry_idx, lbl); \ } default_action_ok_func(action_ok_browse_url_start, ACTION_OK_DL_BROWSE_URL_START) @@ -3283,22 +3225,20 @@ default_action_ok_func(action_ok_push_user_binds_list, ACTION_OK_DL_USER_BINDS_L default_action_ok_func(action_ok_push_accounts_cheevos_list, ACTION_OK_DL_ACCOUNTS_CHEEVOS_LIST) default_action_ok_func(action_ok_open_archive, ACTION_OK_DL_OPEN_ARCHIVE) -static int action_ok_shader_pass(void *data, - const char *path, +static int action_ok_shader_pass(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); menu->hack_shader_pass = type - MENU_SETTINGS_SHADER_PASS_0; - return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, + return generic_action_ok_displaylist_push(path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_SHADER_PASS); } -static int action_ok_netplay_connect_room(void *data, - const char *path, +static int action_ok_netplay_connect_room(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -3345,8 +3285,8 @@ static int action_ok_netplay_connect_room(void *data, return 0; } -static int action_ok_netplay_lan_scan(void *data, - const char *path, + +static int action_ok_netplay_lan_scan(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -3373,12 +3313,12 @@ static int action_ok_netplay_lan_scan(void *data, } #define default_action_ok_dl_push(funcname, _fbid, _id, _path) \ -static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ settings_t *settings = config_get_ptr(); \ (void)settings; \ filebrowser_set_type(_fbid); \ - return generic_action_ok_displaylist_push(data, path, _path, label, type, idx, entry_idx, _id); \ + return generic_action_ok_displaylist_push(path, _path, label, type, idx, entry_idx, _id); \ } default_action_ok_dl_push(action_ok_content_collection_list, FILEBROWSER_SELECT_COLLECTION, ACTION_OK_DL_CONTENT_COLLECTION_LIST, NULL) @@ -3647,8 +3587,7 @@ static void netplay_lan_scan_callback(void *task_data, } } -static int action_ok_push_netplay_refresh_rooms(void *data, - const char *path, +static int action_ok_push_netplay_refresh_rooms(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { char url [2048] = "http://newlobby.libretro.com/list/"; @@ -3661,83 +3600,76 @@ static int action_ok_push_netplay_refresh_rooms(void *data, #endif -static int action_ok_scan_directory_list(void *data, - const char *path, +static int action_ok_scan_directory_list(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { settings_t *settings = config_get_ptr(); filebrowser_clear_type(); - return generic_action_ok_displaylist_push(data, path, + return generic_action_ok_displaylist_push(path, settings->paths.directory_menu_content, label, type, idx, entry_idx, ACTION_OK_DL_SCAN_DIR_LIST); } -static int action_ok_push_random_dir(void *data, - const char *path, +static int action_ok_push_random_dir(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - return generic_action_ok_displaylist_push(data, path, path, + return generic_action_ok_displaylist_push(path, path, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES), type, idx, entry_idx, ACTION_OK_DL_CONTENT_LIST); } -static int action_ok_push_downloads_dir(void *data, - const char *path, +static int action_ok_push_downloads_dir(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { settings_t *settings = config_get_ptr(); filebrowser_set_type(FILEBROWSER_SELECT_FILE); - return generic_action_ok_displaylist_push(data, path, + return generic_action_ok_displaylist_push(path, settings->paths.directory_core_assets, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES), type, idx, entry_idx, ACTION_OK_DL_CONTENT_LIST); } -int action_ok_push_filebrowser_list_dir_select(void *data, - const char *path, +int action_ok_push_filebrowser_list_dir_select(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); filebrowser_set_type(FILEBROWSER_SELECT_DIR); strlcpy(menu->filebrowser_label, label, sizeof(menu->filebrowser_label)); - return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, + return generic_action_ok_displaylist_push(path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_FILE_BROWSER_SELECT_DIR); } -int action_ok_push_filebrowser_list_file_select(void *data, - const char *path, +int action_ok_push_filebrowser_list_file_select(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); filebrowser_set_type(FILEBROWSER_SELECT_FILE); strlcpy(menu->filebrowser_label, label, sizeof(menu->filebrowser_label)); - return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, + return generic_action_ok_displaylist_push(path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_FILE_BROWSER_SELECT_DIR); } -static int action_ok_push_default(void *data, - const char *path, +static int action_ok_push_default(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { filebrowser_clear_type(); - return generic_action_ok_displaylist_push(data, path, NULL, label, type, idx, + return generic_action_ok_displaylist_push(path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_PUSH_DEFAULT); } -static int action_ok_start_core(void *data, - const char *path, +static int action_ok_start_core(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { content_ctx_info_t content_info; @@ -3754,19 +3686,18 @@ static int action_ok_start_core(void *data, return 0; } -static int action_ok_load_archive(void *data, - const char *path, +static int action_ok_load_archive(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - const char *menu_path = NULL; - const char *content_path = NULL; - menu_handle_t *menu = (menu_handle_t*)data; + menu_handle_t *menu = NULL; + const char *menu_path = NULL; + const char *content_path = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); - menu_path = menu->scratch2_buf; - content_path = menu->scratch_buf; + menu_path = menu->scratch2_buf; + content_path = menu->scratch_buf; fill_pathname_join(menu->detect_content_path, menu_path, content_path, @@ -3779,36 +3710,35 @@ static int action_ok_load_archive(void *data, CORE_TYPE_PLAIN); } -static int action_ok_load_archive_detect_core(void *data, - const char *path, +static int action_ok_load_archive_detect_core(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { menu_content_ctx_defer_info_t def_info; - int ret = 0; - core_info_list_t *list = NULL; - const char *menu_path = NULL; - const char *content_path = NULL; - char *new_core_path = NULL; - menu_handle_t *menu = (menu_handle_t*)data; + int ret = 0; + core_info_list_t *list = NULL; + menu_handle_t *menu = NULL; + const char *menu_path = NULL; + const char *content_path = NULL; + char *new_core_path = NULL; - if (!menu) + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); - menu_path = menu->scratch2_buf; - content_path = menu->scratch_buf; + menu_path = menu->scratch2_buf; + content_path = menu->scratch_buf; core_info_get_list(&list); - def_info.data = list; - def_info.dir = menu_path; - def_info.path = content_path; - def_info.menu_label = label; - def_info.s = menu->deferred_path; - def_info.len = sizeof(menu->deferred_path); + def_info.data = list; + def_info.dir = menu_path; + def_info.path = content_path; + def_info.menu_label = label; + def_info.s = menu->deferred_path; + def_info.len = sizeof(menu->deferred_path); - new_core_path = (char*) + new_core_path = (char*) malloc(PATH_MAX_LENGTH * sizeof(char)); - new_core_path[0] = '\0'; + new_core_path[0] = '\0'; if (menu_content_find_first_core(&def_info, false, new_core_path, PATH_MAX_LENGTH * sizeof(char))) @@ -3840,7 +3770,7 @@ static int action_ok_load_archive_detect_core(void *data, break; case 0: idx = menu_navigation_get_selection(); - ret = generic_action_ok_displaylist_push(data, path, NULL, + ret = generic_action_ok_displaylist_push(path, NULL, label, type, idx, entry_idx, ACTION_OK_DL_DEFERRED_CORE_LIST); break; @@ -3853,9 +3783,9 @@ static int action_ok_load_archive_detect_core(void *data, } #define default_action_ok_help(funcname, _id, _id2) \ -static int (funcname)(void *data, const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ +static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ { \ - return generic_action_ok_help(data, path, label, type, idx, entry_idx, _id, _id2); \ + return generic_action_ok_help(path, label, type, idx, entry_idx, _id, _id2); \ } default_action_ok_help(action_ok_help_audio_video_troubleshooting, MENU_ENUM_LABEL_HELP_AUDIO_VIDEO_TROUBLESHOOTING, MENU_DIALOG_HELP_AUDIO_VIDEO_TROUBLESHOOTING) @@ -3866,8 +3796,7 @@ default_action_ok_help(action_ok_help_scanning_content, MENU_ENUM_LABEL_HELP_SCA default_action_ok_help(action_ok_help_change_virtual_gamepad, MENU_ENUM_LABEL_HELP_CHANGE_VIRTUAL_GAMEPAD, MENU_DIALOG_HELP_CHANGE_VIRTUAL_GAMEPAD) default_action_ok_help(action_ok_help_load_content, MENU_ENUM_LABEL_HELP_LOADING_CONTENT, MENU_DIALOG_HELP_LOADING_CONTENT) -static int action_ok_video_resolution(void *data, - const char *path, +static int action_ok_video_resolution(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { unsigned width = 0; @@ -3897,8 +3826,7 @@ static int action_ok_video_resolution(void *data, return 0; } -static int action_ok_netplay_enable_host(void *data, - const char *path, +static int action_ok_netplay_enable_host(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -3970,8 +3898,7 @@ static void action_ok_netplay_enable_client_hostname_cb( } #endif -static int action_ok_netplay_enable_client(void *data, - const char *path, +static int action_ok_netplay_enable_client(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -3992,8 +3919,7 @@ static int action_ok_netplay_enable_client(void *data, return -1; } -static int action_ok_netplay_disconnect(void *data, - const char *path, +static int action_ok_netplay_disconnect(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING @@ -4016,8 +3942,7 @@ static int action_ok_netplay_disconnect(void *data, #endif } -static int action_ok_core_delete(void *data, - const char *path, +static int action_ok_core_delete(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char *path_core = path_get(RARCH_PATH_CORE); diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 15c8205bf7..7a6bc5e047 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -107,11 +107,7 @@ static int action_select_default(const char *path, const char *label, unsigned t static int action_select_path_use_directory(const char *path, const char *label, unsigned type, size_t idx) { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return menu_cbs_exit(); - return action_ok_path_use_directory(menu, - path, label, type, idx, 0 /* unused */); + return action_ok_path_use_directory(path, label, type, idx, 0 /* unused */); } static int action_select_driver_setting(const char *path, const char *label, unsigned type, diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index b5f14aa2c3..3be1f16e85 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -133,26 +133,21 @@ int action_refresh_default(file_list_t *list, file_list_t *menu_list); int shader_action_parameter_right(unsigned type, const char *label, bool wraparound); -int generic_action_ok_displaylist_push(void *data, - const char *path, const char *new_path, +int generic_action_ok_displaylist_push(const char *path, const char *new_path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned action_type); -int action_ok_push_generic_list(void *data, - const char *path, - const char *label, unsigned type, size_t idx, size_t entry_idx); - -int action_ok_path_use_directory(void *data, - const char *path, - const char *label, unsigned type, size_t idx, size_t entry_idx); - -int action_ok_directory_push(void *data, - const char *path, - const char *label, unsigned type, size_t idx, size_t entry_idx); - int generic_action_cheat_toggle(size_t idx, unsigned type, const char *label, bool wraparound); +int action_ok_push_generic_list(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx); + +int action_ok_path_use_directory(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx); + +int action_ok_directory_push(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx); int core_setting_right(unsigned type, const char *label, bool wraparound); diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 82b2932143..ddbadabe02 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -1706,8 +1706,7 @@ bool menu_driver_iterate(menu_ctx_iterate_t *iterate) menu_entries_flush_stack(NULL, MENU_SETTINGS); menu_display_set_msg_force(true); - generic_action_ok_displaylist_push(menu_driver_data, - "", NULL, + generic_action_ok_displaylist_push("", NULL, "", 0, 0, 0, ACTION_OK_DL_CONTENT_SETTINGS); if (menu_driver_pending_quit) diff --git a/menu/menu_entries.h b/menu/menu_entries.h index 3712dc4d1b..c669f9b2af 100644 --- a/menu/menu_entries.h +++ b/menu/menu_entries.h @@ -107,8 +107,7 @@ typedef struct menu_file_list_cbs size_t idx); int (*action_get_title)(const char *path, const char *label, unsigned type, char *s, size_t len); - int (*action_ok)(void *data, - const char *path, const char *label, unsigned type, + int (*action_ok)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx); int (*action_cancel)(const char *path, const char *label, unsigned type, size_t idx); diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index c0c28b8fdd..ea5be0c4cc 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -448,15 +448,9 @@ int menu_entry_action(menu_entry_t *entry, unsigned i, enum menu_action action) break; case MENU_ACTION_OK: - { - menu_handle_t *menu = NULL; - - menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu); - - if (cbs && cbs->action_ok) - ret = cbs->action_ok(menu, entry->path, - entry->label, entry->type, i, entry->entry_idx); - } + if (cbs && cbs->action_ok) + ret = cbs->action_ok(entry->path, + entry->label, entry->type, i, entry->entry_idx); break; case MENU_ACTION_START: if (cbs && cbs->action_start) From 74a3677eb5447e8920bf9b68313fca08116520cc Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Apr 2018 06:14:55 +0200 Subject: [PATCH 274/517] Silence warning --- menu/drivers/zarch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu/drivers/zarch.c b/menu/drivers/zarch.c index 609341055f..495f1eab7d 100644 --- a/menu/drivers/zarch.c +++ b/menu/drivers/zarch.c @@ -1084,7 +1084,7 @@ static bool zarch_load_image(void *userdata, &zui->textures.bg); break; case MENU_IMAGE_THUMBNAIL: - break; + case MENU_IMAGE_LEFT_THUMBNAIL: case MENU_IMAGE_SAVESTATE_THUMBNAIL: /* TODO/FIXME -implement */ break; From 9252c56142a82b2dfa2b763b94718498f0813eb3 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Wed, 11 Apr 2018 17:10:46 -0500 Subject: [PATCH 275/517] Change Windows-1252 encoded file back to UTF-8 --- intl/msg_hash_pt_br.h | 1670 ++++++++++++++++++++--------------------- 1 file changed, 835 insertions(+), 835 deletions(-) diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index 6ed40a144f..73513133b9 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -1,4 +1,4 @@ -MSG_HASH(MSG_COMPILER, +MSG_HASH(MSG_COMPILER, "Compilador" ) MSG_HASH(MSG_UNKNOWN_COMPILER, @@ -11,16 +11,16 @@ MSG_HASH(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED, "Comando Netplay desconhecido recebido" ) MSG_HASH(MSG_FILE_ALREADY_EXISTS_SAVING_TO_BACKUP_BUFFER, - "Este arquivo j existe. Salvando no buffer de backup" + "Este arquivo já existe. Salvando no buffer de backup" ) MSG_HASH(MSG_GOT_CONNECTION_FROM, - "Conexo recebida de: \"%s\"" + "Conexão recebida de: \"%s\"" ) MSG_HASH(MSG_GOT_CONNECTION_FROM_NAME, - "Conexo recebida de: \"%s (%s)\"" + "Conexão recebida de: \"%s (%s)\"" ) MSG_HASH(MSG_PUBLIC_ADDRESS, - "Endereo pblico" + "Endereço público" ) MSG_HASH(MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "Nenhum argumento fornecido e nenhum menu interno, exibindo ajuda..." @@ -32,14 +32,14 @@ MSG_HASH(MSG_WAITING_FOR_CLIENT, "Aguardando pelo cliente..." ) MSG_HASH(MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME, - "Voc deixou o jogo" + "Você deixou o jogo" ) MSG_HASH(MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, - "Voc se juntou como jogador %u" + "Você se juntou como jogador %u" ) MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S, - "Voc se juntou aos dispositivos de entrada %.*s" + "Você se juntou aos dispositivos de entrada %.*s" ) MSG_HASH( MSG_NETPLAY_PLAYER_S_LEFT, @@ -55,29 +55,29 @@ MSG_HASH( ) MSG_HASH( MSG_NETPLAY_NOT_RETROARCH, - "Uma tentativa de conexo com o netplay falhou porque o par no est executando o RetroArch ou est executando uma verso antiga do RetroArch." + "Uma tentativa de conexão com o netplay falhou porque o par não está executando o RetroArch ou está executando uma versão antiga do RetroArch." ) MSG_HASH( MSG_NETPLAY_OUT_OF_DATE, - "O par netplay est executando uma verso antiga do RetroArch. No pode conectar." + "O par netplay está executando uma versão antiga do RetroArch. Não pode conectar." ) MSG_HASH( MSG_NETPLAY_DIFFERENT_VERSIONS, - "ATENO: Um par de Netplay est executando uma verso diferente do RetroArch. Se ocorrerem problemas, use a mesma verso." + "ATENÇÃO: Um par de Netplay está executando uma versão diferente do RetroArch. Se ocorrerem problemas, use a mesma versão." ) MSG_HASH( MSG_NETPLAY_DIFFERENT_CORES, - "Um par de netplay est executando um ncleo diferente. No pode conectar." + "Um par de netplay está executando um núcleo diferente. Não pode conectar." ) MSG_HASH( MSG_NETPLAY_DIFFERENT_CORE_VERSIONS, - "ATENO: Um par de Netplay est executando uma verso diferente do ncleo. Se ocorrerem problemas, use a mesma verso." + "ATENÇÃO: Um par de Netplay está executando uma versão diferente do núcleo. Se ocorrerem problemas, use a mesma versão." ) MSG_HASH(MSG_NETPLAY_ENDIAN_DEPENDENT, - "Este ncleo no suporta Netplay inter-arquitetura entre estes sistemas" + "Este núcleo não suporta Netplay inter-arquitetura entre estes sistemas" ) MSG_HASH(MSG_NETPLAY_PLATFORM_DEPENDENT, - "Este ncleo no suporta Netplay inter-arquitetura" + "Este núcleo não suporta Netplay inter-arquitetura" ) MSG_HASH(MSG_NETPLAY_ENTER_PASSWORD, "Digite a senha do servidor de Netplay:" @@ -95,13 +95,13 @@ MSG_HASH(MSG_NETPLAY_CLIENT_HANGUP, "Desconectado do Netplay" ) MSG_HASH(MSG_NETPLAY_CANNOT_PLAY_UNPRIVILEGED, - "Voc no tem permisso para jogar" + "Você não tem permissão para jogar" ) MSG_HASH(MSG_NETPLAY_CANNOT_PLAY_NO_SLOTS, - "No h vagas livres para jogadores" + "Não há vagas livres para jogadores" ) MSG_HASH(MSG_NETPLAY_CANNOT_PLAY, - "Impossvel alterar para modo jogador" + "Impossível alterar para modo jogador" ) MSG_HASH(MSG_NETPLAY_PEER_PAUSED, "Par do Netplay \"%s\" pausou" @@ -110,19 +110,19 @@ MSG_HASH(MSG_NETPLAY_CHANGED_NICK, "Seu apelido mudou para \"%s\"" ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHARED_CONTEXT, - "Dar aos ncleos renderizados por hardware seu prprio contexto privado. Evita ter que assumir mudanas de estado de hardware entre quadros." + "Dar aos núcleos renderizados por hardware seu próprio contexto privado. Evita ter que assumir mudanças de estado de hardware entre quadros." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SETTINGS, - "Ajusta as configuraes de aparncia da tela de menu." + "Ajusta as configurações de aparência da tela de menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC, - "Sincronia rgida entre CPU e GPU. Reduz a latncia ao custo de desempenho." + "Sincronia rígida entre CPU e GPU. Reduz a latência ao custo de desempenho." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_THREADED, - "Melhora o desempenho ao custo de latncia e mais engasgamento de vdeo. Use somente se voc no puder obter velocidade total de outra forma." + "Melhora o desempenho ao custo de latência e mais engasgamento de vídeo. Use somente se você não puder obter velocidade total de outra forma." ) MSG_HASH(MSG_AUDIO_VOLUME, - "Volume de udio" + "Volume de áudio" ) MSG_HASH(MSG_AUTODETECT, "Autodetectar" @@ -140,10 +140,10 @@ MSG_HASH(MSG_CONNECTING_TO_PORT, "Conectando a porta" ) MSG_HASH(MSG_CONNECTION_SLOT, - "Vaga de conexo" + "Vaga de conexão" ) MSG_HASH(MSG_SORRY_UNIMPLEMENTED_CORES_DONT_DEMAND_CONTENT_NETPLAY, - "Desculpe, no implementado: ncleos que no exigem contedo no podem participar do Netplay." + "Desculpe, não implementado: núcleos que não exigem conteúdo não podem participar do Netplay." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_PASSWORD, "Senha" @@ -152,7 +152,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_SETTINGS, "Contas Cheevos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_USERNAME, - "Nome de usurio" + "Nome de usuário" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ACCOUNTS_LIST, "Contas" @@ -170,13 +170,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_LIST_HARDCORE, "Lista de Conquistas (Hardcore)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_CONTENT_LIST, - "Analisar Contedo" + "Analisar Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIGURATIONS_LIST, - "Configuraes" + "Configurações" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TAB, - "Importar contedo" + "Importar conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_TAB, "Salas de Netplay" @@ -191,49 +191,49 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_BLOCK_FRAMES, "Bloquear Quadros" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_DEVICE, - "Dispositivo de udio" + "Dispositivo de Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_DRIVER, - "Driver de udio" + "Driver de Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_DSP_PLUGIN, - "Plugin DSP de udio" + "Plugin DSP de Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE, - "Habilitar udio" + "Habilitar Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_FILTER_DIR, - "Filtro de udio" + "Filtro de Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_TURBO_DEADZONE_LIST, "Turbo/Zona-Morta" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_LATENCY, - "Latncia de udio (ms)" + "Latência de Áudio (ms)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MAX_TIMING_SKEW, - "Desvio Mximo de Tempo do udio" + "Desvio Máximo de Tempo do Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MUTE, - "udio Mudo" + "Áudio Mudo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_OUTPUT_RATE, - "Taxa da Sada de udio (Hz)" + "Taxa da Saída de Áudio (Hz)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_RATE_CONTROL_DELTA, - "Controle Dinmico da Taxa de udio" + "Controle Dinâmico da Taxa de Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_DRIVER, - "Driver de Reamostragem de udio" + "Driver de Reamostragem de Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_SETTINGS, - "udio" + "Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_SYNC, - "Sincronizar udio" + "Sincronizar Áudio" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_VOLUME, - "Nvel de Volume de udio (dB)" + "Nível de Volume de Áudio (dB)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_WASAPI_EXCLUSIVE_MODE, "WASAPI Modo Exclusivo" @@ -248,13 +248,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTOSAVE_INTERVAL, "Intervalo do Autossalvamento da SRAM" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTO_OVERRIDES_ENABLE, - "Autocarregar Arquivos de Redefinio" + "Autocarregar Arquivos de Redefinição" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTO_REMAPS_ENABLE, "Autocarregar Arquivos de Remapeamento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTO_SHADERS_ENABLE, - "Autocarregar Predefinies de Shader" + "Autocarregar Predefinições de Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_BACK, "Voltar" @@ -263,7 +263,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_CONFIRM, "Confirmar" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_INFO, - "Informaes" + "Informações" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_QUIT, "Sair" @@ -284,13 +284,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_TOGGLE_MENU, "Alternar Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS, - "Controles Bsicos de Menu" + "Controles Básicos de Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_CONFIRM, "Confirmar/OK" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_INFO, - "Informao" + "Informação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_QUIT, "Sair" @@ -299,7 +299,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_SCROLL_UP, "Rolar para Cima" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_START, - "Padres" + "Padrões" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_TOGGLE_KEYBOARD, "Alternar Teclado" @@ -308,7 +308,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_TOGGLE_MENU, "Alternar Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BLOCK_SRAM_OVERWRITE, - "No sobregravar a SRAM ao carregar Estado de Jogo" + "Não sobregravar a SRAM ao carregar Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BLUETOOTH_ENABLE, "Habilitar Bluetooth" @@ -320,45 +320,45 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CACHE_DIRECTORY, "Cache" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CAMERA_ALLOW, - "Permitir Cmera" + "Permitir Câmera" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CAMERA_DRIVER, - "Driver de Cmera" + "Driver de Câmera" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT, - "Trapaa" + "Trapaça" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_APPLY_CHANGES, - "Aplicar Alteraes" + "Aplicar Alterações" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_DATABASE_PATH, - "Arquivo de Trapaa" + "Arquivo de Trapaça" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_FILE, - "Arquivo de Trapaa" + "Arquivo de Trapaça" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_FILE_LOAD, - "Carregar Arquivo de Trapaa" + "Carregar Arquivo de Trapaça" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_FILE_SAVE_AS, - "Salvar Arquivo de Trapaa Como" + "Salvar Arquivo de Trapaça Como" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEAT_NUM_PASSES, - "Estgios de Trapaa" + "Estágios de Trapaça" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_DESCRIPTION, - "Descrio" + "Descrição" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE, "Conquistas no Modo Hardcore" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_LEADERBOARDS_ENABLE, - "Tabelas de Classificao" + "Tabelas de Classificação" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_BADGES_ENABLE, - "Insgnias de Conquistas" + "Insígnias de Conquistas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ACHIEVEMENTS, "Conquistas Bloqueadas:" @@ -370,7 +370,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_SETTINGS, "Retro Achievements" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_TEST_UNOFFICIAL, - "Testar Conquistas No Oficiais" + "Testar Conquistas Não Oficiais" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ACHIEVEMENTS, "Conquistas Desbloqueadas:" @@ -388,59 +388,59 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CHEEVOS_AUTO_SCREENSHOT, - "Captura de Conquistas Automtica" + "Captura de Conquistas Automática" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CLOSE_CONTENT, - "Fechar Contedo" + "Fechar Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIG, - "Configurao" + "Configuração" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIGURATIONS, - "Carregar Configurao" + "Carregar Configuração" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIGURATION_SETTINGS, - "Configurao" + "Configuração" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONFIG_SAVE_ON_EXIT, - "Salvar Configurao ao Sair" + "Salvar Configuração ao Sair" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_COLLECTION_LIST, - "Colees" + "Coleções" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_DATABASE_DIRECTORY, "Base de Dados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_DIR, - "Contedo" + "Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_HISTORY_SIZE, - "Tamanho da Lista de Histrico" + "Tamanho da Lista de Histórico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_ENTRY_REMOVE, - "Permitir a remoo de itens" + "Permitir a remoção de itens" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SETTINGS, - "Menu Rpido" + "Menu Rápido" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ASSETS_DIR, - "Recursos de Ncleo" + "Recursos de Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ASSETS_DIRECTORY, "Downloads" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_CHEAT_OPTIONS, - "Trapaas" + "Trapaças" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_COUNTERS, - "Contadores do Ncleo" + "Contadores do Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ENABLE, - "Exibir nome do ncleo" + "Exibir nome do núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFORMATION, - "Informao do Ncleo" + "Informação do Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_AUTHORS, "Autores" @@ -449,22 +449,22 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CATEGORIES, "Categorias" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_LABEL, - "Rtulo do ncleo" + "Rótulo do núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_NAME, - "Nome do ncleo" + "Nome do núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_FIRMWARE, "Firmware(s)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_LICENSES, - "Licena(s)" + "Licença(s)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_PERMISSIONS, - "Permisses" + "Permissões" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_SUPPORTED_EXTENSIONS, - "Extenses suportadas" + "Extensões suportadas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_MANUFACTURER, "Fabricante do sistema" @@ -476,25 +476,25 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INPUT_REMAPPING_OPTIONS, "Controles" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_LIST, - "Carregar Ncleo" + "Carregar Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_OPTIONS, - "Opes" + "Opções" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_SETTINGS, - "Ncleo" + "Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_SET_SUPPORTS_NO_CONTENT_ENABLE, - "Iniciar um Ncleo Automaticamente" + "Iniciar um Núcleo Automaticamente" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE, "Extrair automaticamente o arquivo baixado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_UPDATER_BUILDBOT_URL, - "URL de Ncleos do Buildbot" + "URL de Núcleos do Buildbot" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_UPDATER_LIST, - "Atualizador de Ncleo" + "Atualizador de Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_UPDATER_SETTINGS, "Atualizador" @@ -512,79 +512,79 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CURSOR_MANAGER, "Gerenciar Cursor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CUSTOM_RATIO, - "Proporo Personalizada" + "Proporção Personalizada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_MANAGER, "Gerenciar Base de Dados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_SELECTION, - "Seleo de Base de Dados" + "Seleção de Base de Dados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DELETE_ENTRY, "Remover" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FAVORITES, - "Diretrio Inicial" + "Diretório Inicial" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_CONTENT, - "" + "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_DEFAULT, - "" + "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_NONE, "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_NOT_FOUND, - "Diretrio no encontrado." + "Diretório não encontrado." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_SETTINGS, - "Diretrio" + "Diretório" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_CYCLE_TRAY_STATUS, - "Condio da Bandeja do Ciclo de Disco" + "Condição da Bandeja do Ciclo de Disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_IMAGE_APPEND, "Anexar Imagem de Disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_INDEX, - "ndice de Disco" + "Índice de Disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DISK_OPTIONS, "Controle de Disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DONT_CARE, - "No importa" + "Não importa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST, "Downloads" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE, - "Baixar Ncleo..." + "Baixar Núcleo..." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE_CONTENT, - "Download de Contedo" + "Download de Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DPI_OVERRIDE_ENABLE, - "Habilitar Redefinio de DPI" + "Habilitar Redefinição de DPI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DPI_OVERRIDE_VALUE, - "Redefinio de DPI" + "Redefinição de DPI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DRIVER_SETTINGS, "Driver" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DUMMY_ON_CORE_SHUTDOWN, - "Carregar Modelo no Desligamento do Ncleo" + "Carregar Modelo no Desligamento do Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHECK_FOR_MISSING_FIRMWARE, "Verificar por Firmware que Falta Antes de Carregar" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DYNAMIC_WALLPAPER, - "Plano de Fundo Dinmico" + "Plano de Fundo Dinâmico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DYNAMIC_WALLPAPERS_DIRECTORY, - "Planos de Fundo Dinmicos" + "Planos de Fundo Dinâmicos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_ENABLE, "Habilitar Conquistas" @@ -599,7 +599,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_FALSE, "Falso" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FASTFORWARD_RATIO, - "Velocidade Mxima de Execuo" + "Velocidade Máxima de Execução" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FAVORITES_TAB, "Favoritos" @@ -608,7 +608,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_FPS_SHOW, "Mostrar Taxa de Quadros" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_ENABLE, - "Controlar Velocidade Mxima de Execuo" + "Controlar Velocidade Máxima de Execução" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_SETTINGS, "Controle de Quadros" @@ -617,43 +617,43 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_FRONTEND_COUNTERS, "Contadores do Frontend" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS, - "Autocarregar Opes de Ncleo Especficas do Contedo" + "Autocarregar Opções de Núcleo Específicas do Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE, - "Criar arquivo de opes do jogo" + "Criar arquivo de opções do jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE, - "Arquivo de opes do jogo" + "Arquivo de opções do jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP, "Ajuda" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_AUDIO_VIDEO_TROUBLESHOOTING, - "Soluo de Problemas de udio/Vdeo" + "Solução de Problemas de Áudio/Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_CHANGE_VIRTUAL_GAMEPAD, - "Alterando a Transparncia de Gamepad Virtual" + "Alterando a Transparência de Gamepad Virtual" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_CONTROLS, - "Controles Bsicos de Menu" + "Controles Básicos de Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_LIST, "Ajuda" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_LOADING_CONTENT, - "Carregando Contedo" + "Carregando Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_SCANNING_CONTENT, - "Procurando em Busca de Contedo" + "Procurando em Busca de Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_WHAT_IS_A_CORE, - "O Que Um Ncleo?" + "O Que É Um Núcleo?" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HISTORY_LIST_ENABLE, - "Habilitar Lista de Histrico" + "Habilitar Lista de Histórico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HISTORY_TAB, - "Histrico" + "Histórico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_HORIZONTAL_MENU, "Menu Horizontal" @@ -662,52 +662,52 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_IMAGES_TAB, "Imagem" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INFORMATION, - "Informao" + "Informação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INFORMATION_LIST, - "Informao" + "Informação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ADC_TYPE, - "Tipo de Analgico Para Digital" + "Tipo de Analógico Para Digital" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ALL_USERS_CONTROL_MENU, - "Todos os Usurios Controlam o Menu" + "Todos os Usuários Controlam o Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X, - "Analgico Esquerdo X" + "Analógico Esquerdo X" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_MINUS, - "Analgico Esquerdo X- (esquerda)" + "Analógico Esquerdo X- (esquerda)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_X_PLUS, - "Analgico Esquerdo X+ (direita)" + "Analógico Esquerdo X+ (direita)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y, - "Analgico Esquerdo Y" + "Analógico Esquerdo Y" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_MINUS, - "Analgico Esquerdo Y- (cima)" + "Analógico Esquerdo Y- (cima)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_LEFT_Y_PLUS, - "Analgico Esquerdo Y+ (baixo)" + "Analógico Esquerdo Y+ (baixo)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X, - "Analgico Direito X" + "Analógico Direito X" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X_MINUS, - "Analgico Direito X- (esquerda)" + "Analógico Direito X- (esquerda)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_X_PLUS, - "Analgico Direito X+ (direita)" + "Analógico Direito X+ (direita)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y, - "Analgico Direito Y" + "Analógico Direito Y" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_MINUS, - "Analgico Direito Y- (cima)" + "Analógico Direito Y- (cima)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_PLUS, - "Analgico Direito Y+ (baixo)" + "Analógico Direito Y+ (baixo)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_TRIGGER, "Gatinho da Pistola") @@ -732,37 +732,37 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_LEFT, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_RIGHT, "D-pad Direito da Pistola") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_AUTODETECT_ENABLE, - "Habilitar Autoconfigurao" + "Habilitar Autoconfiguração" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_AXIS_THRESHOLD, - "Zona Morta do Controle Analgico" + "Zona Morta do Controle Analógico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_INPUT_SWAP_OK_CANCEL, - "Inverter Botes OK e Cancelar do Menu" + "Inverter Botões OK e Cancelar do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_ALL, "Vincular Todos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_DEFAULT_ALL, - "Vincular Todos pelo Padro" + "Vincular Todos pelo Padrão" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_TIMEOUT, "Tempo Limite para Vincular" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DESCRIPTOR_HIDE_UNBOUND, - "Ocultar Descritores de Entrada do Ncleo No Vinculados" + "Ocultar Descritores de Entrada do Núcleo Não Vinculados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DESCRIPTOR_LABEL_SHOW, - "Exibir Rtulos do Descritor de Entrada" + "Exibir Rótulos do Descritor de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_INDEX, - "ndice de Dispositivo" + "Índice de Dispositivo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_TYPE, "Tipo de Dispositivo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_INDEX, - "ndice de Mouse" + "Índice de Mouse" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DRIVER, "Driver de Entrada" @@ -771,58 +771,58 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DUTY_CYCLE, "Ciclo de Trabalho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_HOTKEY_BINDS, - "Vnculos das Teclas de Atalho da Entrada" + "Vínculos das Teclas de Atalho da Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_ICADE_ENABLE, "Habilitar Mapeamento de Gamepad no Teclado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_A, - "Boto A (direita)" + "Botão A (direita)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_B, - "Boto B (baixo)" + "Botão B (baixo)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_DOWN, "Direcional para baixo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_L2, - "Boto L2 (gatilho)" + "Botão L2 (gatilho)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_L3, - "Boto L3 (polegar)" + "Botão L3 (polegar)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_L, - "Boto L (ombro)" + "Botão L (ombro)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_LEFT, "Direcional Esquerdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_R2, - "Boto R2 (gatilho)" + "Botão R2 (gatilho)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_R3, - "Boto R3 (polegar)" + "Botão R3 (polegar)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_R, - "Boto R (ombro)" + "Botão R (ombro)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_RIGHT, "Direcional Direito" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_SELECT, - "Boto Select" + "Botão Select" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_START, - "Boto Start" + "Botão Start" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_UP, "Direcional para Cima" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_X, - "Boto X (topo)" + "Botão X (topo)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_Y, - "Boto Y (esquerda)" + "Botão Y (esquerda)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_KEY, "(Tecla: %s)" @@ -849,25 +849,25 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_KEYBOARD_GAMEPAD_MAPPING_TYPE, "Tipo de Mapeamento para Gamepad no Teclado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_MAX_USERS, - "Usurios Mximos" + "Usuários Máximos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_MENU_ENUM_TOGGLE_GAMEPAD_COMBO, - "Combinao do Gamepad para Alternar Menu" + "Combinação do Gamepad para Alternar Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_CHEAT_INDEX_MINUS, - "ndice de Trapaa -" + "Índice de Trapaça -" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_CHEAT_INDEX_PLUS, - "ndice de Trapaa +" + "Índice de Trapaça +" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_CHEAT_TOGGLE, - "Alternar Trapaa" + "Alternar Trapaça" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_EJECT_TOGGLE, - "Alternar ejeo de disco" + "Alternar ejeção de disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_NEXT, - "Prximo disco" + "Próximo disco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_PREV, "Disco anterior" @@ -876,13 +876,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_ENABLE_HOTKEY, "Habilitar teclas de atalho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_FAST_FORWARD_HOLD_KEY, - "Manter Avano Rpido" + "Manter Avanço Rápido" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_FAST_FORWARD_KEY, - "Alternar Avano Rpido" + "Alternar Avanço Rápido" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_FRAMEADVANCE, - "Avano de Quadro" + "Avanço de Quadro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_FULLSCREEN_TOGGLE_KEY, "Alternar tela cheia" @@ -900,10 +900,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, "Alternar menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, - "Alternar gravao de filme" + "Alternar gravação de filme" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, - "Alternar udio mudo" + "Alternar áudio mudo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Alternar modo jogador/espectador do Netplay" @@ -912,7 +912,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, "Alternar teclado virtual" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OVERLAY_NEXT, - "Prxima Transparncia" + "Próxima Transparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE, "Alternar pausa" @@ -933,13 +933,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SCREENSHOT, "Capturar tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_NEXT, - "Prximo Shader" + "Próximo Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_PREV, "Shader Anterior" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION_HOLD_KEY, - "Cmera Lenta" + "Câmera Lenta" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, "Compartimento do Estado de Jogo -" @@ -954,13 +954,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_VOLUME_UP, "Volume +" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_ENABLE, - "Mostrar Transparncia" + "Mostrar Transparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_HIDE_IN_MENU, - "Ocultar Transparncia no Menu" + "Ocultar Transparência no Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS, - "Exibir Comandos Na Transparncia" + "Exibir Comandos Na Transparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS_PORT, "Porta de Escuta do Exibir Comandos " @@ -984,10 +984,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_REMAPPING_DIRECTORY, "Remapeamento de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_REMAP_BINDS_ENABLE, - "Habilitar Remapeamento de Vnculos" + "Habilitar Remapeamento de Vínculos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_SAVE_AUTOCONFIG, - "Salvar Autoconfigurao" + "Salvar Autoconfiguração" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_SETTINGS, "Entrada" @@ -1002,61 +1002,61 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_TURBO_ENABLE, "Habilitar Turbo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_TURBO_PERIOD, - "Perodo do Turbo" + "Período do Turbo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_USER_BINDS, - "Vnculos de Entrada do Usurio %u" + "Vínculos de Entrada do Usuário %u" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INTERNAL_STORAGE_STATUS, - "Condio do armazenamento interno" + "Condição do armazenamento interno" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_JOYPAD_AUTOCONFIG_DIR, - "Autoconfigurao de Entrada" + "Autoconfiguração de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_JOYPAD_DRIVER, "Driver de Joypad" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LAKKA_SERVICES, - "Servios" + "Serviços" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_CHINESE_SIMPLIFIED, - "Chins (Simplificado)" + "Chinês (Simplificado)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_CHINESE_TRADITIONAL, - "Chins (Tradicional)" + "Chinês (Tradicional)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_DUTCH, - "Holands" + "Holandês" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ENGLISH, - "Ingls" + "Inglês" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ESPERANTO, "Esperanto" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_FRENCH, - "Francs" + "Francês" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_GERMAN, - "Alemo" + "Alemão" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_ITALIAN, "Italiano" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_JAPANESE, - "Japons" + "Japonês" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_KOREAN, "Coreano" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_POLISH, - "Polons" + "Polonês" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_PORTUGUESE_BRAZIL, - "Portugus (Brasil)" + "Português (Brasil)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_PORTUGUESE_PORTUGAL, - "Portugus (Portugal)" + "Português (Portugal)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LANG_RUSSIAN, "Russo" @@ -1072,16 +1072,16 @@ MSG_HASH( "Arabic" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LEFT_ANALOG, - "Analgico Esquerdo" + "Analógico Esquerdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LIBRETRO_DIR_PATH, - "Ncleo" + "Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LIBRETRO_INFO_PATH, - "Informao do Ncleo" + "Informação do Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LIBRETRO_LOG_LEVEL, - "Nvel de Registro de Eventos do Ncleo" + "Nível de Registro de Eventos do Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LINEAR, "Linear" @@ -1093,16 +1093,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_HISTORY, "Carregar Recente" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_LIST, - "Carregar Contedo" + "Carregar Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOAD_STATE, "Carregar Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOCATION_ALLOW, - "Permitir Localizao" + "Permitir Localização" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOCATION_DRIVER, - "Driver de Localizao" + "Driver de Localização" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_LOGGING_SETTINGS, "Registro de Eventos" @@ -1114,7 +1114,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MAIN_MENU, "Menu Principal" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MANAGEMENT, - "Configuraes da Base de Dados" + "Configurações da Base de Dados" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME, "Tema de Cor do Menu" @@ -1141,10 +1141,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_YELLOW, "Amarelo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_FOOTER_OPACITY, - "Opacidade do Rodap" + "Opacidade do Rodapé" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_HEADER_OPACITY, - "Opacidade do Cabealho" + "Opacidade do Cabeçalho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_DRIVER, "Driver de Menu" @@ -1153,16 +1153,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_ENUM_THROTTLE_FRAMERATE, "Controlar Taxa de Quadros do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_FILE_BROWSER_SETTINGS, - "Configuraes" + "Configurações" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_LINEAR_FILTER, "Filtro Linear de Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_HORIZONTAL_ANIMATION, - "Animao Horizontal" + "Animação Horizontal" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SETTINGS, - "Aparncia" + "Aparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_WALLPAPER, "Plano de Fundo" @@ -1180,19 +1180,19 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MOUSE_ENABLE, "Suporte para Mouse" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MULTIMEDIA_SETTINGS, - "Multimdia" + "Multimídia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MUSIC_TAB, - "Msica" + "Música" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE, - "Filtrar Extenses Desconhecidas" + "Filtrar Extensões Desconhecidas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NAVIGATION_WRAPAROUND, - "Navegao Retorna ao Incio" + "Navegação Retorna ao Início" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NEAREST, - "Mais Prximo" + "Mais Próximo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY, "Netplay" @@ -1204,10 +1204,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES, "Verificar Quadros do Netplay" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, - "Quadros de Latncia de Entrada" + "Quadros de Latência de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, - "Faixa de Quadros de Latncia de Entrada" + "Faixa de Quadros de Latência de Entrada" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Atraso de Quadros do Netplay" @@ -1228,7 +1228,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISABLE_HOST, "Parar hospedeiro de Netplay" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_IP_ADDRESS, - "Endereo do Servidor" + "Endereço do Servidor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_LAN_SCAN_SETTINGS, "Analisar a rede local" @@ -1237,7 +1237,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_MODE, "Habilitar Cliente Netplay" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_NICKNAME, - "Usurio" + "Usuário" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD, "Senha do Servidor" @@ -1247,16 +1247,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE, MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUEST_DEVICE_I, "Solicitar Dispositivo %u") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUIRE_SLAVES, - "No Permitir Clientes em Modo No Escravo" + "Não Permitir Clientes em Modo Não Escravo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS, - "Configuraes do Netplay") + "Configurações do Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG, - "Compartilhamento de Entrada Analgica") + "Compartilhamento de Entrada Analógica") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_MAX, - "Mximo") + "Máximo") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_AVERAGE, - "Mdio") + "Médio") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL, "Compartilhamento de Entrada Digital") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_OR, @@ -1268,7 +1268,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_VOTE, MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NONE, "Nenhum") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NO_PREFERENCE, - "Sem preferncia") + "Sem preferência") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR, "Modo Espectador do Netplay" ) @@ -1294,7 +1294,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_PORT, "Porta de Comando de Rede" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_INFORMATION, - "Informao de Rede" + "Informação de Rede" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_REMOTE_ENABLE, "Gamepad de Rede" @@ -1306,7 +1306,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_SETTINGS, "Rede" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO, - "No" + "Não" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NONE, "Nenhum" @@ -1315,28 +1315,28 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE, "N/D" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_ACHIEVEMENTS_TO_DISPLAY, - "No h Conquistas para mostrar." + "Não há Conquistas para mostrar." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_CORE, - "Nenhum Ncleo" + "Nenhum Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_CORES_AVAILABLE, - "Nenhum ncleo disponvel" + "Nenhum núcleo disponível" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_CORE_INFORMATION_AVAILABLE, - "No h informao de ncleo disponvel." + "Não há informação de núcleo disponível." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_CORE_OPTIONS_AVAILABLE, - "No h opes de ncleo disponveis." + "Não há opções de núcleo disponíveis." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY, - "No h itens para mostrar." + "Não há itens para mostrar." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_HISTORY_AVAILABLE, - "No h histrico disponvel." + "Não há histórico disponível." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE, - "No h informao disponvel." + "Não há informação disponível." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_ITEMS, "Sem itens." @@ -1348,19 +1348,19 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_NETWORKS_FOUND, "Nenhuma rede encontrada." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_PERFORMANCE_COUNTERS, - "No h contadores de desempenho." + "Não há contadores de desempenho." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_PLAYLISTS, - "No h listas de reproduo." + "Não há listas de reprodução." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_PLAYLIST_ENTRIES_AVAILABLE, - "No h itens de lista de reproduo disponveis." + "Não há itens de lista de reprodução disponíveis." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND, - "Nenhuma configurao encontrada." + "Nenhuma configuração encontrada." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_SHADER_PARAMETERS, - "No h parmetros de Shader." + "Não há parâmetros de Shader." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OFF, "DESLIGADO" @@ -1375,13 +1375,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ONLINE_UPDATER, "Atualizador Online" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ONSCREEN_DISPLAY_SETTINGS, - "Exibio na Tela" + "Exibição na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ONSCREEN_OVERLAY_SETTINGS, - "Transparncia na Tela" + "Transparência na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ONSCREEN_NOTIFICATIONS_SETTINGS, - "Notificaes na Tela" + "Notificações na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OPEN_ARCHIVE, "Navegar no Arquivo" @@ -1390,49 +1390,49 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_OPTIONAL, "Opcional" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY, - "Transparncia" + "Transparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_AUTOLOAD_PREFERRED, - "Autocarregar Transparncia Favorita" + "Autocarregar Transparência Favorita" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_DIRECTORY, - "Transparncia" + "Transparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_OPACITY, - "Opacidade da Transparncia" + "Opacidade da Transparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_PRESET, - "Predefinio de Transparncia" + "Predefinição de Transparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_SCALE, - "Escala da Transparncia" + "Escala da Transparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_OVERLAY_SETTINGS, - "Transparncia na Tela" + "Transparência na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PAL60_ENABLE, "Utilizar Modo PAL60" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PARENT_DIRECTORY, - "Diretrio superior" + "Diretório superior" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PAUSE_LIBRETRO, "Pausar quando o menu for ativado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PAUSE_NONACTIVE, - "No rodar em segundo plano" + "Não rodar em segundo plano" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PERFCNT_ENABLE, "Contadores de Desempenho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB, - "Listas de Reproduo" + "Listas de Reprodução" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_DIRECTORY, - "Lista de Reproduo" + "Lista de Reprodução" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_SETTINGS, - "Listas de Reproduo" + "Listas de Reprodução" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_POINTER_ENABLE, "Suporte para Toque" @@ -1450,13 +1450,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_QUIT_RETROARCH, "Sair do RetroArch" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ANALOG, - "Analgico suportado" + "Analógico suportado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_BBFC_RATING, - "Classificao BBFC" + "Classificação BBFC" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CERO_RATING, - "Classificao CERO" + "Classificação CERO" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_COOP, "Cooperativo suportado" @@ -1465,37 +1465,37 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CRC32, "CRC32" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DESCRIPTION, - "Descrio" + "Descrição" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DEVELOPER, "Desenvolvedor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_EDGE_MAGAZINE_ISSUE, - "Edio da Revista Edge" + "Edição da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_EDGE_MAGAZINE_RATING, - "Classificao da Revista Edge" + "Classificação da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_EDGE_MAGAZINE_REVIEW, - "Anlise da Revista Edge" + "Análise da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ELSPA_RATING, - "Classificao ELSPA" + "Classificação ELSPA" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ENHANCEMENT_HW, "Hardware de Aprimoramento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ESRB_RATING, - "Classificao ESRB" + "Classificação ESRB" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_FAMITSU_MAGAZINE_RATING, - "Classificao da Revista Famitsu" + "Classificação da Revista Famitsu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_FRANCHISE, "Franquia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_GENRE, - "Gnero" + "Gênero" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_MD5, "MD5" @@ -1507,58 +1507,58 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ORIGIN, "Origem" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PEGI_RATING, - "Classificao PEGI" + "Classificação PEGI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PUBLISHER, "Editor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_RELEASE_MONTH, - "Ms de Lanamento" + "Mês de Lançamento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_RELEASE_YEAR, - "Ano de Lanamento" + "Ano de Lançamento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_RUMBLE, - "Suporte para Vibrao" + "Suporte para Vibração" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_SERIAL, - "Nmero de Srie" + "Número de Série" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_SHA1, "SHA1" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_START_CONTENT, - "Iniciar Contedo" + "Iniciar Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_TGDB_RATING, - "Classificao TGDB" + "Classificação TGDB" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REBOOT, "Reiniciar" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORDING_CONFIG_DIRECTORY, - "Configurao de Gravao" + "Configuração de Gravação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORDING_OUTPUT_DIRECTORY, - "Sada de Gravao" + "Saída de Gravação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORDING_SETTINGS, - "Gravao" + "Gravação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_CONFIG, - "Carregar Configurao de Gravao..." + "Carregar Configuração de Gravação..." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_DRIVER, - "Driver de Gravao" + "Driver de Gravação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_ENABLE, - "Habilitar Gravao" + "Habilitar Gravação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_PATH, - "Salvar Sada de Gravao Como..." + "Salvar Saída de Gravação Como..." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_USE_OUTPUT_DIRECTORY, - "Salvar Gravaes no Diretrio de Sada" + "Salvar Gravações no Diretório de Saída" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE, "Arquivo de Remapeamento" @@ -1567,19 +1567,19 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_LOAD, "Carregar Arquivo de Remapeamento" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_SAVE_CORE, - "Salvar Arquivo de Remapeamento de Ncleo" + "Salvar Arquivo de Remapeamento de Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_SAVE_GAME, "Salvar Arquivo de Remapeamento de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_REMOVE_CORE, - "Remover Arquivo de Remapeamento de Ncleo" + "Remover Arquivo de Remapeamento de Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_REMOVE_GAME, "Remover Arquivo de Remapeamento de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REQUIRED, - "Obrigatrio" + "Obrigatório" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RESTART_CONTENT, "Reiniciar" @@ -1600,31 +1600,31 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROPAD, "RetroPad" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROPAD_WITH_ANALOG, - "RetroPad com Analgico" + "RetroPad com Analógico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RETRO_ACHIEVEMENTS_SETTINGS, "Conquistas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_ENABLE, - "Habilitar Voltar Atrs" + "Habilitar Voltar Atrás" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_GRANULARITY, - "Granularidade do Voltar Atrs" + "Granularidade do Voltar Atrás" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_SETTINGS, - "Voltar Atrs" + "Voltar Atrás" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RGUI_BROWSER_DIRECTORY, "Navegador de Arquivos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RGUI_CONFIG_DIRECTORY, - "Configurao" + "Configuração" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RGUI_SHOW_START_SCREEN, "Mostrar Tela Inicial" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_RIGHT_ANALOG, - "Analgico Direito" + "Analógico Direito" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_FAVORITES, "Adicionar aos Favoritos" @@ -1645,7 +1645,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVEFILE_DIRECTORY, "Arquivo de Jogo-Salvo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATE_AUTO_INDEX, - "ndice Automtico de Estado de Jogo" + "Índice Automático de Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATE_AUTO_LOAD, "Autocarregar Estado de Jogo" @@ -1660,16 +1660,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATE_THUMBNAIL_ENABLE, "Miniaturas do Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG, - "Salvar Configurao Atual" + "Salvar Configuração Atual" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG_OVERRIDE_CORE, - "Salvar Redefinio de Ncleo" + "Salvar Redefinição de Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG_OVERRIDE_GAME, - "Salvar Redefinio de Jogo" + "Salvar Redefinição de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_NEW_CONFIG, - "Salvar Nova Configurao" + "Salvar Nova Configuração" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVE_STATE, "Salvar Estado de Jogo" @@ -1678,19 +1678,19 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVING_SETTINGS, "Salvando" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCAN_DIRECTORY, - "Analisar Diretrio" + "Analisar Diretório" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCAN_FILE, "Analisar Arquivo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCAN_THIS_DIRECTORY, - "" + "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCREENSHOT_DIRECTORY, "Captura de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCREEN_RESOLUTION, - "Resoluo da Tela" + "Resolução da Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SEARCH, "Procurar" @@ -1699,16 +1699,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SECONDS, "segundos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SETTINGS, - "Configuraes" + "Configurações" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SETTINGS_TAB, - "Configuraes" + "Configurações" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER, "Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_APPLY_CHANGES, - "Aplicar Alteraes" + "Aplicar Alterações" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS, "Shaders" @@ -1726,7 +1726,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_PIPELINE_SNOW, "Neve" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHOW_ADVANCED_SETTINGS, - "Exibir Configuraes Avanadas" + "Exibir Configurações Avançadas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SHOW_HIDDEN_FILES, "Exibir Arquivos e Pastas Ocultos" @@ -1735,7 +1735,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SHUTDOWN, "Desligar" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SLOWMOTION_RATIO, - "Taxa de Cmera Lenta" + "Taxa de Câmera Lenta" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVEFILES_ENABLE, "Classificar Arquivos de Jogo-Salvo em Pastas" @@ -1744,52 +1744,52 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVESTATES_ENABLE, "Classificar Arquivos de Estado de Jogo em Pastas" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVESTATES_IN_CONTENT_DIR_ENABLE, - "Gravar Estados de Jogo no Diretrio de Contedo" + "Gravar Estados de Jogo no Diretório de Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SAVEFILES_IN_CONTENT_DIR_ENABLE, - "Gravar Jogos-Salvos no Diretrio de Contedo" + "Gravar Jogos-Salvos no Diretório de Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEMFILES_IN_CONTENT_DIR_ENABLE, - "Arquivos de Sistema esto no Diretrio de Contedo" + "Arquivos de Sistema estão no Diretório de Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SCREENSHOTS_IN_CONTENT_DIR_ENABLE, - "Salvar Capturas de Tela no Diretrio de Contedo" + "Salvar Capturas de Tela no Diretório de Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SSH_ENABLE, "Habilitar SSH" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_START_CORE, - "Iniciar Ncleo" + "Iniciar Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_START_NET_RETROPAD, "Iniciar RetroPad Remoto" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_START_VIDEO_PROCESSOR, - "Iniciar Processador de Vdeo" + "Iniciar Processador de Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_STATE_SLOT, "Compartimento do Estado de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_STATUS, - "Condio" + "Condição" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_STDIN_CMD_ENABLE, "Comandos stdin" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SUPPORTED_CORES, - "Ncleos Sugeridos" + "Núcleos Sugeridos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SUSPEND_SCREENSAVER_ENABLE, "Desativar Protetor de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_BGM_ENABLE, - "Habilitar Msica em Segundo Plano do Sistema" + "Habilitar Música em Segundo Plano do Sistema" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_DIRECTORY, "Sistema/BIOS" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFORMATION, - "Informao do Sistema" + "Informação do Sistema" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_7ZIP_SUPPORT, "Suporte a 7zip" @@ -1798,7 +1798,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_ALSA_SUPPORT, "Suporte a ALSA" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_BUILD_DATE, - "Data de Compilao" + "Data de Compilação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_CG_SUPPORT, "Suporte a Cg" @@ -1807,22 +1807,22 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COCOA_SUPPORT, "Suporte a Cocoa" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COMMAND_IFACE_SUPPORT, - "Suporte Interface de Comando" + "Suporte à Interface de Comando" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_CORETEXT_SUPPORT, "Suporte a CoreText" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_CPU_FEATURES, - "Caractersticas de CPU" + "Características de CPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_DPI, - "Mtrica DPI da Tela" + "Métrica DPI da Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_HEIGHT, - "Mtrica de Altura da Tela (mm)" + "Métrica de Altura da Tela (mm)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, - "Mtrica de Largura da Tela (mm)" + "Métrica de Largura da Tela (mm)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "Suporte a DirectSound" @@ -1831,10 +1831,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, "Suporte a WASAPI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, - "Suporte biblioteca dinmica" + "Suporte à biblioteca dinâmica" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, - "Carregamento dinmico em tempo de execuo da biblioteca libretro" + "Carregamento dinâmico em tempo de execução da biblioteca libretro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_EGL_SUPPORT, "Suporte a EGL" @@ -1858,7 +1858,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_FRONTEND_OS, "SO do Frontend" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_GIT_VERSION, - "Verso Git" + "Versão Git" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_GLSL_SUPPORT, "Suporte a GLSL" @@ -1873,7 +1873,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_KMS_SUPPORT, "Suporte a KMS/EGL" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LAKKA_VERSION, - "Verso Lakka" + "Versão Lakka" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LIBRETRODB_SUPPORT, "Suporte a LibretroDB" @@ -1888,7 +1888,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_NETPLAY_SUPPORT, "Suporte Netplay (ponto-a-ponto)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_NETWORK_COMMAND_IFACE_SUPPORT, - "Suporte Interface de comando de rede" + "Suporte à Interface de comando de rede" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_NETWORK_REMOTE_SUPPORT, "Suporte a Gamepad de Rede" @@ -1912,7 +1912,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OSS_SUPPORT, "Suporte a OSS" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OVERLAY_SUPPORT, - "Suporte Transparncia" + "Suporte à Transparência" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_POWER_SOURCE, "Fonte de Energia" @@ -1927,7 +1927,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_POWER_SOURCE_DISCHARGING, "Descarregando" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_POWER_SOURCE_NO_SOURCE, - "No h fonte" + "Não há fonte" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_PULSEAUDIO_SUPPORT, "Suporte a PulseAudio" @@ -1939,7 +1939,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RBMP_SUPPORT, "Suporte a BMP (RBMP)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RETRORATING_LEVEL, - "Nvel RetroRating" + "Nível RetroRating" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RJPEG_SUPPORT, "Suporte a JPEG (RJPEG)" @@ -1978,7 +1978,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_V4L2_SUPPORT, "Suporte a Video4Linux2" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_VIDEO_CONTEXT_DRIVER, - "Driver de contexto de vdeo" + "Driver de contexto de vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_VULKAN_SUPPORT, "Suporte a Vulkan" @@ -2007,7 +2007,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_THREADED_DATA_RUNLOOP_ENABLE, MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS, "Miniaturas") MSG_HASH(MENU_ENUM_LABEL_VALUE_LEFT_THUMBNAILS, - "Miniaturas esquerda") + "Miniaturas à esquerda") MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAILS_DIRECTORY, "Miniaturas" ) @@ -2021,22 +2021,22 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_SCREENSHOTS, "Captura de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_TITLE_SCREENS, - "Tela do Ttulo" + "Tela do Título" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_TIMEDATE_ENABLE, "Exibir data / hora" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_TITLE_COLOR, - "Cor do ttulo do menu" + "Cor do título do menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_TRUE, "Verdadeiro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_ENABLE, - "Habilitar Companheiro da Interface de Usurio" + "Habilitar Companheiro da Interface de Usuário" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_START_ON_BOOT, - "Companheiro da Interface de Usurio Roda na Inicializao" + "Companheiro da Interface de Usuário Roda na Inicialização" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_MENUBAR_ENABLE, "Barra de Menu" @@ -2060,16 +2060,16 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_ASSETS, "Atualizar Recursos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_AUTOCONFIG_PROFILES, - "Atualizar Perfis de Autoconfigurao" + "Atualizar Perfis de Autoconfiguração" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_CG_SHADERS, "Atualizar Shaders Cg" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_CHEATS, - "Atualizar Trapaas" + "Atualizar Trapaças" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_CORE_INFO_FILES, - "Atualizar Arquivos de Informao de Ncleo" + "Atualizar Arquivos de Informação de Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_DATABASES, "Atualizar Bases de Dados" @@ -2081,79 +2081,79 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_LAKKA, "Atualizar Lakka" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_OVERLAYS, - "Atualizar Transparncias" + "Atualizar Transparências" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_UPDATE_SLANG_SHADERS, "Atualizar Shaders Slang" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USER, - "Usurio" + "Usuário" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USER_INTERFACE_SETTINGS, - "Interface de Usurio" + "Interface de Usuário" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USER_LANGUAGE, "Idioma" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USER_SETTINGS, - "Usurio" + "Usuário" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USE_BUILTIN_IMAGE_VIEWER, "Utilizar o Visualizador de Imagem Integrado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USE_BUILTIN_PLAYER, - "Utilizar o Reprodutor de Mdia Integrado" + "Utilizar o Reprodutor de Mídia Integrado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_USE_THIS_DIRECTORY, - "" + "" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ALLOW_ROTATE, - "Permitir rotao" + "Permitir rotação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO, - "Configurar Proporo de Tela" + "Configurar Proporção de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_AUTO, - "Proporo de Tela Automtica" + "Proporção de Tela Automática" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_INDEX, - "Proporo de Tela" + "Proporção de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_BLACK_FRAME_INSERTION, - "Insero de Quadro Opaco" + "Inserção de Quadro Opaco" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_CROP_OVERSCAN, "Cortar Overscan (Recarregar)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_DISABLE_COMPOSITION, - "Desativar Composio da rea de Trabalho" + "Desativar Composição da Área de Trabalho" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_DRIVER, - "Driver de Vdeo" + "Driver de Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FILTER, - "Filtro de Vdeo" + "Filtro de Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FILTER_DIR, - "Filtro de Vdeo" + "Filtro de Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FILTER_FLICKER, - "Filtro de tremulao de vdeo" + "Filtro de tremulação de vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FONT_ENABLE, - "Habilitar Notificaes na Tela" + "Habilitar Notificações na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FONT_PATH, - "Fonte das Notificaes na Tela" + "Fonte das Notificações na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FONT_SIZE, - "Tamanho da Notificao na Tela" + "Tamanho da Notificação na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_ASPECT, - "Forar Proporo de Tela" + "Forçar Proporção de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_SRGB_DISABLE, - "Forar Desativao de sRGB FBO" + "Forçar Desativação de sRGB FBO" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_DELAY, "Atraso de Quadro" @@ -2162,43 +2162,43 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_FULLSCREEN, "Utilizar Modo de Tela Cheia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_GAMMA, - "Gama de Vdeo" + "Gama de Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_GPU_RECORD, - "Usar Gravao da GPU" + "Usar Gravação da GPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_GPU_SCREENSHOT, "Habilitar Captura de Tela da GPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC, - "Sincronia Rgida de GPU" + "Sincronia Rígida de GPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC_FRAMES, - "Quadros de Sincronia Rgida de GPU" + "Quadros de Sincronia Rígida de GPU" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MAX_SWAPCHAIN_IMAGES, - "Mximo de imagens na cadeia de troca" + "Máximo de imagens na cadeia de troca" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_POS_X, - "Posio X da Notificao na Tela" + "Posição X da Notificação na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_POS_Y, - "Posio Y da Notificao na Tela" + "Posição Y da Notificação na Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MONITOR_INDEX, - "ndice de Monitor" + "Índice de Monitor" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_POST_FILTER_RECORD, - "Usar Gravao Ps-Filtro" + "Usar Gravação Pós-Filtro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, - "Taxa de Atualizao Vertical" + "Taxa de Atualização Vertical" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Taxa de Quadros Estimada da Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, - "Rotao" + "Rotação" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, "Escala em Janela" @@ -2207,28 +2207,28 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE_INTEGER, "Escala em Inteiros" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SETTINGS, - "Vdeo" + "Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_DIR, - "Shader de Vdeo" + "Shader de Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_NUM_PASSES, - "Estgios de Shader" + "Estágios de Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PARAMETERS, - "Pr-visualizar Parmetros de Shader" + "Pré-visualizar Parâmetros de Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET, - "Carregar Predefinio de Shader" + "Carregar Predefinição de Shader" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS, - "Salvar Predefinio de Shader Como" + "Salvar Predefinição de Shader Como" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_CORE, - "Salvar Predefinio de Ncleo" + "Salvar Predefinição de Núcleo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_GAME, - "Salvar Predefinio de Jogo" + "Salvar Predefinição de Jogo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SHARED_CONTEXT, "Habilitar Contexto Compartilhado de Hardware" @@ -2240,34 +2240,34 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SOFT_FILTER, "Habilitar Filtro por Software" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SWAP_INTERVAL, - "Intervalo de Troca da Sincronizao Vertical (V-Sync)" + "Intervalo de Troca da Sincronização Vertical (V-Sync)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_TAB, - "Vdeo" + "Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_THREADED, - "Vdeo Paralelizado" + "Vídeo Paralelizado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VFILTER, - "Reduzir Tremulao de Vdeo" + "Reduzir Tremulação de Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_HEIGHT, - "Altura Personalizada da Proporo de Tela" + "Altura Personalizada da Proporção de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_WIDTH, - "Largura Personalizada da Proporo de Tela" + "Largura Personalizada da Proporção de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_X, - "Posio X Personalizada da Proporo de Tela" + "Posição X Personalizada da Proporção de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_Y, - "Posio Y Personalizada da Proporo de Tela" + "Posição Y Personalizada da Proporção de Tela" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VI_WIDTH, "Definir Largura de Tela do VI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VSYNC, - "Sincronizao Vertical (V-Sync)" + "Sincronização Vertical (V-Sync)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOWED_FULLSCREEN, "Modo Janela em Tela Cheia" @@ -2303,13 +2303,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_FLATUI, "FlatUI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_MONOCHROME, - "Monocromtico" + "Monocromático" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_MONOCHROME_INVERTED, - "Monocromtico Inverted" + "Monocromático Inverted" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_SYSTEMATIC, - "Sistemtico" + "Sistemático" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_NEOACTIVE, "NeoActive" @@ -2330,7 +2330,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME, "Tema de Cor do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_APPLE_GREEN, - "Verde Ma" + "Verde Maçã" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_DARK, "Escuro" @@ -2339,7 +2339,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_DARK_PURPLE, "Roxo Escuro" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_ELECTRIC_BLUE, - "Azul Eltrico" + "Azul Elétrico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_GOLDEN, "Dourado" @@ -2357,7 +2357,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_UNDERSEA, "Submarino" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_VOLCANIC_RED, - "Vermelho Vulcnico" + "Vermelho Vulcânico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_RIBBON_ENABLE, "Pipeline do Shader de Menu" @@ -2366,13 +2366,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SCALE_FACTOR, "Fator de Escala do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SHADOWS_ENABLE, - "Habilitar Sombras dos cones" + "Habilitar Sombras dos Ícones" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_HISTORY, - "Exibir Aba de Histrico" + "Exibir Aba de Histórico" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_ADD, - "Exibir Aba de Importao de Contedo" + "Exibir Aba de Importação de Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_FAVORITES, "Exibir Aba de Favoritos" @@ -2381,101 +2381,101 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_IMAGES, "Exibir Aba de Imagem" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_MUSIC, - "Exibir Aba de Msica" + "Exibir Aba de Música" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS, - "Exibir Aba de Configuraes" + "Exibir Aba de Configurações" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, - "Exibir Aba de Vdeo" + "Exibir Aba de Vídeo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Exibir Aba de Netplay" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, - "Tema de cones do Menu" + "Tema de Ícones do Menu" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, "Sim" ) MSG_HASH(MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_TWO, - "Predefinio de Shader" + "Predefinição de Shader" ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_ENABLE, - "Habilitar ou desabilitar conquistas. Para mais informaes, visite http://retroachievements.org" + "Habilitar ou desabilitar conquistas. Para mais informações, visite http://retroachievements.org" ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, - "Habilitar ou desabilitar conquistas no oficiais e/ou recursos beta para fins de teste." + "Habilitar ou desabilitar conquistas não oficiais e/ou recursos beta para fins de teste." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, - "Habilitar ou desabilitar Estado de Jogo, Trapaas, Voltar Atrs, Avano Rpido, Pausa e Cmera Lenta para todos os jogos.") + "Habilitar ou desabilitar Estado de Jogo, Trapaças, Voltar Atrás, Avanço Rápido, Pausa e Câmera Lenta para todos os jogos.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_LEADERBOARDS_ENABLE, - "Ativar ou desativar tabelas de classificao no jogo. No tem efeito se o modo Hardcore estiver desativado.") + "Ativar ou desativar tabelas de classificação no jogo. Não tem efeito se o modo Hardcore estiver desativado.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_BADGES_ENABLE, - "Ativar ou desativar a exibio de insgnia na Lista de Conquistas.") + "Ativar ou desativar a exibição de insígnia na Lista de Conquistas.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_VERBOSE_ENABLE, "Habilitar ou desabilitar detalhes das conquistas na tela.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_AUTO_SCREENSHOT, - "Obter automaticamente uma captura de tela quando uma conquista acionada.") + "Obter automaticamente uma captura de tela quando uma conquista é acionada.") MSG_HASH(MENU_ENUM_SUBLABEL_DRIVER_SETTINGS, "Alterar os drivers utilizados pelo sistema." ) MSG_HASH(MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS, - "Alterar as configuraes de conquistas." + "Alterar as configurações de conquistas." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_SETTINGS, - "Alterar as configuraes de ncleo." + "Alterar as configurações de núcleo." ) MSG_HASH(MENU_ENUM_SUBLABEL_RECORDING_SETTINGS, - "Alterar as configuraes de gravao." + "Alterar as configurações de gravação." ) MSG_HASH(MENU_ENUM_SUBLABEL_ONSCREEN_DISPLAY_SETTINGS, - "Alterar as configuraes de Transparncia e Transparncia de teclado, e as configuraes de notificao na tela." + "Alterar as configurações de Transparência e Transparência de teclado, e as configurações de notificação na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_FRAME_THROTTLE_SETTINGS, - "Alterar as configuraes de Voltar Atrs, Avano Rpido e Cmera Lenta." + "Alterar as configurações de Voltar Atrás, Avanço Rápido e Câmera Lenta." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVING_SETTINGS, - "Alterar as configuraes de salvamento." + "Alterar as configurações de salvamento." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOGGING_SETTINGS, - "Alterar as configuraes de registro de eventos." + "Alterar as configurações de registro de eventos." ) MSG_HASH(MENU_ENUM_SUBLABEL_USER_INTERFACE_SETTINGS, - "Alterar as configuraes da interface de usurio." + "Alterar as configurações da interface de usuário." ) MSG_HASH(MENU_ENUM_SUBLABEL_USER_SETTINGS, - "Alterar as configuraes de conta, nome de usurio e idioma." + "Alterar as configurações de conta, nome de usuário e idioma." ) MSG_HASH(MENU_ENUM_SUBLABEL_PRIVACY_SETTINGS, - "Alterar as configuraes de privacidade." + "Alterar as configurações de privacidade." ) MSG_HASH(MENU_ENUM_SUBLABEL_DIRECTORY_SETTINGS, - "Alterar os diretrios padro onde os arquivos esto localizados." + "Alterar os diretórios padrão onde os arquivos estão localizados." ) MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_SETTINGS, - "Alterar as configuraes de lista de reproduo." + "Alterar as configurações de lista de reprodução." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETWORK_SETTINGS, - "Configurar as configuraes de servidor e rede." + "Configurar as configurações de servidor e rede." ) MSG_HASH(MENU_ENUM_SUBLABEL_ADD_CONTENT_LIST, - "Analisar contedo e adicionar na base de dados." + "Analisar conteúdo e adicionar na base de dados." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_SETTINGS, - "Alterar as configuraes de sada de udio." + "Alterar as configurações de saída de áudio." ) MSG_HASH(MENU_ENUM_SUBLABEL_BLUETOOTH_ENABLE, "Habilitar ou desabilitar o bluetooth." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONFIG_SAVE_ON_EXIT, - "Salvar as alteraes nos arquivos de configurao ao sair." + "Salvar as alterações nos arquivos de configuração ao sair." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONFIGURATION_SETTINGS, - "Alterar as definies padro para os arquivos de configurao." + "Alterar as definições padrão para os arquivos de configuração." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONFIGURATIONS_LIST, - "Gerenciar e criar arquivos de configurao." + "Gerenciar e criar arquivos de configuração." ) MSG_HASH(MENU_ENUM_SUBLABEL_CPU_CORES, "Quantidade de Cores que a CPU possui." @@ -2484,76 +2484,76 @@ MSG_HASH(MENU_ENUM_SUBLABEL_FPS_SHOW, "Exibir a taxa atual de quadros por segundo na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_HOTKEY_BINDS, - "Ajustar configuraes das teclas de atalho." + "Ajustar configurações das teclas de atalho." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_MENU_ENUM_TOGGLE_GAMEPAD_COMBO, - "Combinao de botes do Gamepad para alternar o menu." + "Combinação de botões do Gamepad para alternar o menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_SETTINGS, - "Alterar as configuraes de Joypad, teclado e Mouse." + "Alterar as configurações de Joypad, teclado e Mouse." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_USER_BINDS, - "Configurar os controles para este usurio." + "Configurar os controles para este usuário." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOG_VERBOSITY, "Habilitar ou desabilitar registro de eventos no terminal." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY, - "Juntar-se ou hospedar uma sesso de Netplay." + "Juntar-se ou hospedar uma sessão de Netplay." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_LAN_SCAN_SETTINGS, "Procurar por e conectar aos hospedeiros de Netplay na rede local." ) MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST, - "Exibir informaes de ncleo, rede e sistema." + "Exibir informações de núcleo, rede e sistema." ) MSG_HASH(MENU_ENUM_SUBLABEL_ONLINE_UPDATER, - "Baixar complementos, componentes e contedo para o RetroArch." + "Baixar complementos, componentes e conteúdo para o RetroArch." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAMBA_ENABLE, "Habilitar ou desabilitar compartilhamento de pastas na rede." ) MSG_HASH(MENU_ENUM_SUBLABEL_SERVICES_SETTINGS, - "Gerenciar servios ao nvel de sistema operacional." + "Gerenciar serviços ao nível de sistema operacional." ) MSG_HASH(MENU_ENUM_SUBLABEL_SHOW_HIDDEN_FILES, - "Exibir arquivos/diretrios ocultos no navegador de arquivos." + "Exibir arquivos/diretórios ocultos no navegador de arquivos." ) MSG_HASH(MENU_ENUM_SUBLABEL_SSH_ENABLE, - "Habilitar ou desabilitar acesso remoto linha de comando." + "Habilitar ou desabilitar acesso remoto à linha de comando." ) MSG_HASH(MENU_ENUM_SUBLABEL_SUSPEND_SCREENSAVER_ENABLE, "Prevenir que o protetor de tela do seu sistema seja ativado." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_WINDOW_SCALE, - "Definir o tamanho da janela em relao ao tamanho da janela de exibio do ncleo. Como alternativa, voc pode definir uma largura e altura de janela abaixo para um tamanho de janela fixo." + "Definir o tamanho da janela em relação ao tamanho da janela de exibição do núcleo. Como alternativa, você pode definir uma largura e altura de janela abaixo para um tamanho de janela fixo." ) MSG_HASH(MENU_ENUM_SUBLABEL_USER_LANGUAGE, "Definir o idioma da interface." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_BLACK_FRAME_INSERTION, - "Inserir um quadro opaco entre quadros. til para usurios com telas de 120Hz que desejam jogar contedos em 60Hz para eliminar efeito de fantasma." + "Inserir um quadro opaco entre quadros. Útil para usuários com telas de 120Hz que desejam jogar conteúdos em 60Hz para eliminar efeito de fantasma." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY, - "Reduz a latncia ao custo de maior risco de engasgamento de vdeo. Adiciona um atraso aps o V-Sync (em ms)." + "Reduz a latência ao custo de maior risco de engasgamento de vídeo. Adiciona um atraso após o V-Sync (em ms)." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC_FRAMES, - "Definir quantos quadros a CPU pode rodar frente da GPU quando utilizado o recurso 'Sincronia Rgida de GPU'." + "Definir quantos quadros a CPU pode rodar à frente da GPU quando utilizado o recurso 'Sincronia Rígida de GPU'." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MAX_SWAPCHAIN_IMAGES, - "Informar ao driver de vdeo para utilizar explicitamente um modo de buffer especfico." + "Informar ao driver de vídeo para utilizar explicitamente um modo de buffer específico." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, - "Seleciona qual tela de exibio a ser usada." + "Seleciona qual tela de exibição a ser usada." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, - "A taxa de atualizao estimada da tela em Hz." + "A taxa de atualização estimada da tela em Hz." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, - "Alterar as configuraes de sada de vdeo." + "Alterar as configurações de saída de vídeo." ) MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, - "Analisar por redes sem fio e estabelecer uma conexo." + "Analisar por redes sem fio e estabelecer uma conexão." ) MSG_HASH(MENU_ENUM_SUBLABEL_HELP_LIST, "Saiba mais sobre como o programa funciona." @@ -2565,28 +2565,28 @@ MSG_HASH(MSG_APPENDED_DISK, "Disco anexado" ) MSG_HASH(MSG_APPLICATION_DIR, - "Diretrio do aplicativo" + "Diretório do aplicativo" ) MSG_HASH(MSG_APPLYING_CHEAT, - "Aplicando as alteraes de Trapaas." + "Aplicando as alterações de Trapaças." ) MSG_HASH(MSG_APPLYING_SHADER, "Aplicando Shader" ) MSG_HASH(MSG_AUDIO_MUTED, - "udio mudo." + "Áudio mudo." ) MSG_HASH(MSG_AUDIO_UNMUTED, - "udio mudo desativado." + "Áudio mudo desativado." ) MSG_HASH(MSG_AUTOCONFIG_FILE_ERROR_SAVING, - "Erro em salvar o arquivo de autoconfigurao." + "Erro em salvar o arquivo de autoconfiguração." ) MSG_HASH(MSG_AUTOCONFIG_FILE_SAVED_SUCCESSFULLY, - "Arquivo de autoconfigurao salvo com sucesso." + "Arquivo de autoconfiguração salvo com sucesso." ) MSG_HASH(MSG_AUTOSAVE_FAILED, - "No foi possvel inicializar o autossalvamento." + "Não foi possível inicializar o autossalvamento." ) MSG_HASH(MSG_AUTO_SAVE_STATE_TO, "Autosalvar Estado de Jogo em" @@ -2601,76 +2601,76 @@ MSG_HASH(MSG_BYTES, "bytes" ) MSG_HASH(MSG_CANNOT_INFER_NEW_CONFIG_PATH, - "No possvel inferir o novo caminho de configurao. Use a hora atual." + "Não é possível inferir o novo caminho de configuração. Use a hora atual." ) MSG_HASH(MSG_CHEEVOS_HARDCORE_MODE_ENABLE, - "Modo Hardcore habilitado, Estados de Jogo e Voltar Atrs esto desabilitados." + "Modo Hardcore habilitado, Estados de Jogo e Voltar Atrás estão desabilitados." ) MSG_HASH(MSG_COMPARING_WITH_KNOWN_MAGIC_NUMBERS, - "Comparando com nmeros mgicos conhecidos..." + "Comparando com números mágicos conhecidos..." ) MSG_HASH(MSG_COMPILED_AGAINST_API, "Compilado contra a API" ) MSG_HASH(MSG_CONFIG_DIRECTORY_NOT_SET, - "Diretrio de configurao no definido. No foi possvel salvar a nova configurao." + "Diretório de configuração não definido. Não foi possível salvar a nova configuração." ) MSG_HASH(MSG_CONNECTED_TO, "Conectado a" ) MSG_HASH(MSG_CONTENT_CRC32S_DIFFER, - "O CRC32 dos contedos difere. No possvel utilizar jogos diferentes." + "O CRC32 dos conteúdos difere. Não é possível utilizar jogos diferentes." ) MSG_HASH(MSG_CONTENT_LOADING_SKIPPED_IMPLEMENTATION_WILL_DO_IT, - "Carregamento de contedo ignorado. A implementao ir carregar por conta prpria." + "Carregamento de conteúdo ignorado. A implementação irá carregar por conta própria." ) MSG_HASH(MSG_CORE_DOES_NOT_SUPPORT_SAVESTATES, - "O ncleo no suporta Estados de Jogo." + "O núcleo não suporta Estados de Jogo." ) MSG_HASH(MSG_CORE_OPTIONS_FILE_CREATED_SUCCESSFULLY, - "O arquivo de opes de ncleo foi criado com sucesso." + "O arquivo de opções de núcleo foi criado com sucesso." ) MSG_HASH(MSG_COULD_NOT_FIND_ANY_NEXT_DRIVER, - "No foi possvel encontrar nenhum driver seguinte" + "Não foi possível encontrar nenhum driver seguinte" ) MSG_HASH(MSG_COULD_NOT_FIND_COMPATIBLE_SYSTEM, - "No foi possvel encontrar um sistema compatvel." + "Não foi possível encontrar um sistema compatível." ) MSG_HASH(MSG_COULD_NOT_FIND_VALID_DATA_TRACK, - "No foi possvel encontrar uma faixa de dados vlida" + "Não foi possível encontrar uma faixa de dados válida" ) MSG_HASH(MSG_COULD_NOT_OPEN_DATA_TRACK, - "No foi possvel abrir a faixa de dados" + "Não foi possível abrir a faixa de dados" ) MSG_HASH(MSG_COULD_NOT_READ_CONTENT_FILE, - "No foi possvel ler o arquivo de contedo" + "Não foi possível ler o arquivo de conteúdo" ) MSG_HASH(MSG_COULD_NOT_READ_MOVIE_HEADER, - "No foi possvel ler o cabealho do filme." + "Não foi possível ler o cabeçalho do filme." ) MSG_HASH(MSG_COULD_NOT_READ_STATE_FROM_MOVIE, - "No foi possvel ler o Estado de Jogo do filme." + "Não foi possível ler o Estado de Jogo do filme." ) MSG_HASH(MSG_CRC32_CHECKSUM_MISMATCH, - "Soma de verificao CRC32 incompatvel entre o arquivo de contedo e a soma de verificao de contedo salva no cabealho do arquivo de reproduo. Reproduo altamente susceptvel de dessincronizar na reproduo." + "Soma de verificação CRC32 incompatível entre o arquivo de conteúdo e a soma de verificação de conteúdo salva no cabeçalho do arquivo de reprodução. Reprodução altamente susceptível de dessincronizar na reprodução." ) MSG_HASH(MSG_CUSTOM_TIMING_GIVEN, "Tempo personalizado fornecido" ) MSG_HASH(MSG_DECOMPRESSION_ALREADY_IN_PROGRESS, - "Descompresso j est em andamento." + "Descompressão já está em andamento." ) MSG_HASH(MSG_DECOMPRESSION_FAILED, - "Descompresso falhou." + "Descompressão falhou." ) MSG_HASH(MSG_DETECTED_VIEWPORT_OF, - "Detectada janela de exibio de" + "Detectada janela de exibição de" ) MSG_HASH(MSG_DID_NOT_FIND_A_VALID_CONTENT_PATCH, - "No encontrou uma modificao de contedo vlido." + "Não encontrou uma modificação de conteúdo válido." ) MSG_HASH(MSG_DISCONNECT_DEVICE_FROM_A_VALID_PORT, - "Desconectar dispositivo de uma porta vlida." + "Desconectar dispositivo de uma porta válida." ) MSG_HASH(MSG_DISK_CLOSED, "Fechado" @@ -2688,16 +2688,16 @@ MSG_HASH(MSG_ERROR, "Erro" ) MSG_HASH(MSG_ERROR_LIBRETRO_CORE_REQUIRES_CONTENT, - "O ncleo libretro requer contedo, mas nada foi fornecido." + "O núcleo libretro requer conteúdo, mas nada foi fornecido." ) MSG_HASH(MSG_ERROR_LIBRETRO_CORE_REQUIRES_SPECIAL_CONTENT, - "O ncleo libretro requer contedo especial, mas nenhum foi fornecido." + "O núcleo libretro requer conteúdo especial, mas nenhum foi fornecido." ) MSG_HASH(MSG_ERROR_PARSING_ARGUMENTS, "Erro em analisar os argumentos." ) MSG_HASH(MSG_ERROR_SAVING_CORE_OPTIONS_FILE, - "Erro em salvar o arquivo de opes de ncleo." + "Erro em salvar o arquivo de opções de núcleo." ) MSG_HASH(MSG_ERROR_SAVING_REMAP_FILE, "Erro em salvar o arquivo de remapeamento." @@ -2706,10 +2706,10 @@ MSG_HASH(MSG_ERROR_REMOVING_REMAP_FILE, "Erro em remover o arquivo de remapeamento." ) MSG_HASH(MSG_ERROR_SAVING_SHADER_PRESET, - "Erro em salvar a predefinio de Shader." + "Erro em salvar a predefinição de Shader." ) MSG_HASH(MSG_EXTERNAL_APPLICATION_DIR, - "Diretrio de Aplicativo Externo" + "Diretório de Aplicativo Externo" ) MSG_HASH(MSG_EXTRACTING, "Extraindo" @@ -2718,7 +2718,7 @@ MSG_HASH(MSG_EXTRACTING_FILE, "Extraindo arquivo" ) MSG_HASH(MSG_FAILED_SAVING_CONFIG_TO, - "Falha em salvar a configurao em" + "Falha em salvar a configuração em" ) MSG_HASH(MSG_FAILED_TO, "Falha em" @@ -2727,7 +2727,7 @@ MSG_HASH(MSG_FAILED_TO_ACCEPT_INCOMING_SPECTATOR, "Falha em aceitar o espectador ingresso." ) MSG_HASH(MSG_FAILED_TO_ALLOCATE_MEMORY_FOR_PATCHED_CONTENT, - "Falha em alocar memria para o contedo modificado..." + "Falha em alocar memória para o conteúdo modificado..." ) MSG_HASH(MSG_FAILED_TO_APPLY_SHADER, "Falha em aplicar o Shader." @@ -2736,10 +2736,10 @@ MSG_HASH(MSG_FAILED_TO_BIND_SOCKET, "Falha em vincular o soquete." ) MSG_HASH(MSG_FAILED_TO_CREATE_THE_DIRECTORY, - "Falha em criar o diretrio." + "Falha em criar o diretório." ) MSG_HASH(MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE, - "Falha em extrair o contedo do arquivo comprimido" + "Falha em extrair o conteúdo do arquivo comprimido" ) MSG_HASH(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT, "Falha em obter o apelido do cliente." @@ -2748,25 +2748,25 @@ MSG_HASH(MSG_FAILED_TO_LOAD, "Falha em carregar" ) MSG_HASH(MSG_FAILED_TO_LOAD_CONTENT, - "Falha em carregar o contedo" + "Falha em carregar o conteúdo" ) MSG_HASH(MSG_FAILED_TO_LOAD_MOVIE_FILE, "Falha em carregar o arquivo de filme" ) MSG_HASH(MSG_FAILED_TO_LOAD_OVERLAY, - "Falha em carregar a Transparncia." + "Falha em carregar a Transparência." ) MSG_HASH(MSG_FAILED_TO_LOAD_STATE, "Falha em carregar o Estado de Jogo de" ) MSG_HASH(MSG_FAILED_TO_OPEN_LIBRETRO_CORE, - "Falha em abrir o ncleo Libretro" + "Falha em abrir o núcleo Libretro" ) MSG_HASH(MSG_FAILED_TO_PATCH, - "Falha em executar a modificao" + "Falha em executar a modificação" ) MSG_HASH(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT, - "Falha em receber o cabealho do cliente." + "Falha em receber o cabeçalho do cliente." ) MSG_HASH(MSG_FAILED_TO_RECEIVE_NICKNAME, "Falha em receber o apelido." @@ -2784,7 +2784,7 @@ MSG_HASH(MSG_FAILED_TO_REMOVE_DISK_FROM_TRAY, "Falha em remover o disco da bandeja." ) MSG_HASH(MSG_FAILED_TO_REMOVE_TEMPORARY_FILE, - "Falha em remover o arquivo temporrio" + "Falha em remover o arquivo temporário" ) MSG_HASH(MSG_FAILED_TO_SAVE_SRAM, "Falha em salvar SRAM" @@ -2808,13 +2808,13 @@ MSG_HASH(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT, "Falha em enviar os dados SRAM para o cliente." ) MSG_HASH(MSG_FAILED_TO_START_AUDIO_DRIVER, - "Falha em iniciar o driver de udio. Prosseguindo sem udio." + "Falha em iniciar o driver de áudio. Prosseguindo sem áudio." ) MSG_HASH(MSG_FAILED_TO_START_MOVIE_RECORD, - "Falha em iniciar a gravao do filme." + "Falha em iniciar a gravação do filme." ) MSG_HASH(MSG_FAILED_TO_START_RECORDING, - "Falha em iniciar a gravao." + "Falha em iniciar a gravação." ) MSG_HASH(MSG_FAILED_TO_TAKE_SCREENSHOT, "Falha em obter uma captura de tela." @@ -2831,60 +2831,60 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_AUTOSAVE_INTERVAL, - "Salvar automaticamente o Save RAM no-voltil em um intervalo regular. Isso est desabilitado por padro, a menos que seja definido de outra forma. O intervalo medido em segundos. Um valor de 0 desativa o salvamento automtico." + "Salvar automaticamente o Save RAM não-volátil em um intervalo regular. Isso está desabilitado por padrão, a menos que seja definido de outra forma. O intervalo é medido em segundos. Um valor de 0 desativa o salvamento automático." ) MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE, - "Se ativado, substitui os vnculos de entrada com as associaes remapeadas definidas para o ncleo atual." + "Se ativado, substitui os vínculos de entrada com as associações remapeadas definidas para o núcleo atual." ) MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_AUTODETECT_ENABLE, - "Ativa a deteco automtica de entrada. Tentar autoconfigurar joypads, estilo Plug-and-Play." + "Ativa a detecção automática de entrada. Tentará autoconfigurar joypads, estilo Plug-and-Play." ) MSG_HASH( MENU_ENUM_SUBLABEL_MENU_INPUT_SWAP_OK_CANCEL, - "Troca de botes para OK/Cancelar. Desabilitado o estilo de boto japons, habilitada oestilo ocidental." + "Troca de botões para OK/Cancelar. Desabilitado é o estilo de botão japonês, habilitada é oestilo ocidental." ) MSG_HASH( MENU_ENUM_SUBLABEL_PAUSE_LIBRETRO, - "Se desabilitado, o contedo continuar sendo executado em segundo plano quando o menu do RetroArch for alternado." + "Se desabilitado, o conteúdo continuará sendo executado em segundo plano quando o menu do RetroArch for alternado." ) MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_DRIVER, - "Driver de vdeo para usar." + "Driver de vídeo para usar." ) MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_DRIVER, - "Driver de udio para usar." + "Driver de áudio para usar." ) MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_DRIVER, - "Driver de entrada para usar. Dependendo do driver de vdeo, pode forar um driver de entrada diferente." + "Driver de entrada para usar. Dependendo do driver de vídeo, pode forçar um driver de entrada diferente." ) MSG_HASH( MENU_ENUM_SUBLABEL_JOYPAD_DRIVER, "Driver do Joypad para usar." ) MSG_HASH(MSG_FAILED_TO_UNMUTE_AUDIO, - "Falha em desativar o udio mudo." + "Falha em desativar o áudio mudo." ) MSG_HASH(MSG_FATAL_ERROR_RECEIVED_IN, "Erro fatal recebido em" ) MSG_HASH(MSG_FILE_NOT_FOUND, - "Arquivo no encontrado" + "Arquivo não encontrado" ) MSG_HASH(MSG_FOUND_AUTO_SAVESTATE_IN, - "Estado de Jogo automtico encontrado em" + "Estado de Jogo automático encontrado em" ) MSG_HASH(MSG_FOUND_DISK_LABEL, - "Rtulo de disco encontrado" + "Rótulo de disco encontrado" ) MSG_HASH(MSG_FOUND_FIRST_DATA_TRACK_ON_FILE, "Encontrada primeira faixa de dados no arquivo" ) MSG_HASH(MSG_FOUND_LAST_STATE_SLOT, - "Encontrada ltimo compartimento de Estado de Jogo" + "Encontrada último compartimento de Estado de Jogo" ) MSG_HASH(MSG_FOUND_SHADER, "Shader encontrado" @@ -2893,10 +2893,10 @@ MSG_HASH(MSG_FRAMES, "Quadros" ) MSG_HASH(MSG_GAME_SPECIFIC_CORE_OPTIONS_FOUND_AT, - "Opes por Jogo: Opes de ncleo especficas do jogo encontradas em" + "Opções por Jogo: Opções de núcleo específicas do jogo encontradas em" ) MSG_HASH(MSG_GOT_INVALID_DISK_INDEX, - "ndice de disco invlido obtido" + "Índice de disco inválido obtido" ) MSG_HASH(MSG_GRAB_MOUSE_STATE, "Capturar estado do Mouse" @@ -2908,22 +2908,22 @@ MSG_HASH(MSG_GAME_FOCUS_OFF, "Foco do jogo desligado" ) MSG_HASH(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING, - "O ncleo libretro renderizado por hardware. Deve usar a gravao ps-Shader tambm." + "O núcleo libretro é renderizado por hardware. Deve usar a gravação pós-Shader também." ) MSG_HASH(MSG_INFLATED_CHECKSUM_DID_NOT_MATCH_CRC32, - "A soma de verificao inflada no corresponde ao CRC32." + "A soma de verificação inflada não corresponde ao CRC32." ) MSG_HASH(MSG_INPUT_CHEAT, - "Entrada de Trapaa" + "Entrada de Trapaça" ) MSG_HASH(MSG_INPUT_CHEAT_FILENAME, - "Nome do Arquivo de Trapaa" + "Nome do Arquivo de Trapaça" ) MSG_HASH(MSG_INPUT_PRESET_FILENAME, - "Nome de Arquivo de Predefinio" + "Nome de Arquivo de Predefinição" ) MSG_HASH(MSG_INPUT_RENAME_ENTRY, - "Renomear Ttulo" + "Renomear Título" ) MSG_HASH(MSG_INTERFACE, "Interface" @@ -2932,10 +2932,10 @@ MSG_HASH(MSG_INTERNAL_STORAGE, "Armazenamento Interno" ) MSG_HASH(MSG_REMOVABLE_STORAGE, - "Armazenamento Removvel" + "Armazenamento Removível" ) MSG_HASH(MSG_INVALID_NICKNAME_SIZE, - "Tamanho de apelido invlido." + "Tamanho de apelido inválido." ) MSG_HASH(MSG_IN_BYTES, "em bytes" @@ -2947,7 +2947,7 @@ MSG_HASH(MSG_IN_MEGABYTES, "em megabytes" ) MSG_HASH(MSG_LIBRETRO_ABI_BREAK, - "foi compilado contra uma verso diferente do libretro do que esta." + "foi compilado contra uma versão diferente do libretro do que esta." ) MSG_HASH(MSG_LIBRETRO_FRONTEND, "Frontend para Libretro" @@ -2956,55 +2956,55 @@ MSG_HASH(MSG_LOADED_STATE_FROM_SLOT, "Estado de Jogo carregado do compartimento #%d." ) MSG_HASH(MSG_LOADED_STATE_FROM_SLOT_AUTO, - "Estado de Jogo carregado do compartimento #-1 (automtico)." + "Estado de Jogo carregado do compartimento #-1 (automático)." ) MSG_HASH(MSG_LOADING, "Carregando" ) MSG_HASH(MSG_FIRMWARE, - "Um ou mais arquivos de firmware esto faltando" + "Um ou mais arquivos de firmware estão faltando" ) MSG_HASH(MSG_LOADING_CONTENT_FILE, - "Carregando arquivo de contedo" + "Carregando arquivo de conteúdo" ) MSG_HASH(MSG_LOADING_HISTORY_FILE, - "Carregando arquivo de histrico" + "Carregando arquivo de histórico" ) MSG_HASH(MSG_LOADING_STATE, "Carregando Estado de Jogo" ) MSG_HASH(MSG_MEMORY, - "Memria" + "Memória" ) MSG_HASH(MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE, - "O arquivo de filme no um arquivo BSV1 vlido." + "O arquivo de filme não é um arquivo BSV1 válido." ) MSG_HASH(MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION, - "O formato de filme parece ter uma verso de serializador diferente. Provavelmente ir falhar." + "O formato de filme parece ter uma versão de serializador diferente. Provavelmente irá falhar." ) MSG_HASH(MSG_MOVIE_PLAYBACK_ENDED, - "Reproduo de filme terminou." + "Reprodução de filme terminou." ) MSG_HASH(MSG_MOVIE_RECORD_STOPPED, - "Parando a gravao de filme." + "Parando a gravação de filme." ) MSG_HASH(MSG_NETPLAY_FAILED, "Falha em inicializar o Netplay." ) MSG_HASH(MSG_NO_CONTENT_STARTING_DUMMY_CORE, - "Sem contedo, iniciando um ncleo modelo." + "Sem conteúdo, iniciando um núcleo modelo." ) MSG_HASH(MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET, - "Nenhum Estado de Jogo foi sobrescrito at o momento." + "Nenhum Estado de Jogo foi sobrescrito até o momento." ) MSG_HASH(MSG_NO_STATE_HAS_BEEN_LOADED_YET, - "Nenhum Estado de Jogo foi carregado at o momento." + "Nenhum Estado de Jogo foi carregado até o momento." ) MSG_HASH(MSG_OVERRIDES_ERROR_SAVING, - "Erro em salvar as redefinies." + "Erro em salvar as redefinições." ) MSG_HASH(MSG_OVERRIDES_SAVED_SUCCESSFULLY, - "Redefinies salvas com sucesso." + "Redefinições salvas com sucesso." ) MSG_HASH(MSG_PAUSED, "Pausado." @@ -3019,13 +3019,13 @@ MSG_HASH(MSG_RECEIVED, "recebido" ) MSG_HASH(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE, - "A gravao terminou devido ao redimensionamento." + "A gravação terminou devido ao redimensionamento." ) MSG_HASH(MSG_RECORDING_TO, "Gravando em" ) MSG_HASH(MSG_REDIRECTING_CHEATFILE_TO, - "Redirecionando o arquivo de Trapaa em" + "Redirecionando o arquivo de Trapaça em" ) MSG_HASH(MSG_REDIRECTING_SAVEFILE_TO, "Redirecionando o Jogo-Salvo em" @@ -3043,49 +3043,49 @@ MSG_HASH(MSG_REMOVED_DISK_FROM_TRAY, "Disco removido da bandeja." ) MSG_HASH(MSG_REMOVING_TEMPORARY_CONTENT_FILE, - "Removendo arquivo de contedo temporrio" + "Removendo arquivo de conteúdo temporário" ) MSG_HASH(MSG_RESET, "Reinicializar" ) MSG_HASH(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT, - "Reiniciando a gravao devido ao reincio do driver." + "Reiniciando a gravação devido ao reinício do driver." ) MSG_HASH(MSG_RESTORED_OLD_SAVE_STATE, "Estado de Jogo antigo restaurado." ) MSG_HASH(MSG_RESTORING_DEFAULT_SHADER_PRESET_TO, - "Shaders: restaurando predefinio padro de Shader em" + "Shaders: restaurando predefinição padrão de Shader em" ) MSG_HASH(MSG_REVERTING_SAVEFILE_DIRECTORY_TO, - "Revertendo diretrio de Jogo-Salvo em" + "Revertendo diretório de Jogo-Salvo em" ) MSG_HASH(MSG_REVERTING_SAVESTATE_DIRECTORY_TO, - "Revertendo diretrio de Estado de Jogo em" + "Revertendo diretório de Estado de Jogo em" ) MSG_HASH(MSG_REWINDING, - "Voltando atrs." + "Voltando atrás." ) MSG_HASH(MSG_REWIND_INIT, - "Inicializando o buffer de Voltar Atrs com tamanho" + "Inicializando o buffer de Voltar Atrás com tamanho" ) MSG_HASH(MSG_REWIND_INIT_FAILED, - "Falha em inicializar o buffer de Voltar Atrs. Voltar Atrs ser desativado." + "Falha em inicializar o buffer de Voltar Atrás. Voltar Atrás será desativado." ) MSG_HASH(MSG_REWIND_INIT_FAILED_THREADED_AUDIO, - "Esta implementao usa udio paralelizado. No possvel utilizar Voltar Atrs." + "Esta implementação usa áudio paralelizado. Não é possível utilizar Voltar Atrás." ) MSG_HASH(MSG_REWIND_REACHED_END, - "Final do buffer de Voltar Atrs atingido." + "Final do buffer de Voltar Atrás atingido." ) MSG_HASH(MSG_SAVED_NEW_CONFIG_TO, - "Nova configurao salva em" + "Nova configuração salva em" ) MSG_HASH(MSG_SAVED_STATE_TO_SLOT, "Estado de Jogo salvo no compartimento #%d." ) MSG_HASH(MSG_SAVED_STATE_TO_SLOT_AUTO, - "Estado de Jogo salvo no compartimento #-1 (automtico)." + "Estado de Jogo salvo no compartimento #-1 (automático)." ) MSG_HASH(MSG_SAVED_SUCCESSFULLY_TO, "Salvo com sucesso em" @@ -3100,40 +3100,40 @@ MSG_HASH(MSG_SCANNING, "Analisando" ) MSG_HASH(MSG_SCANNING_OF_DIRECTORY_FINISHED, - "Anlise de diretrio terminada" + "Análise de diretório terminada" ) MSG_HASH(MSG_SENDING_COMMAND, "Enviando comando" ) MSG_HASH(MSG_SEVERAL_PATCHES_ARE_EXPLICITLY_DEFINED, - "Vrias modificaes de contedo esto explicitamente definidas, ignorando todas..." + "Várias modificações de conteúdo estão explicitamente definidas, ignorando todas..." ) MSG_HASH(MSG_SHADER, "Shader" ) MSG_HASH(MSG_SHADER_PRESET_SAVED_SUCCESSFULLY, - "Predefinio de Shader salva com sucesso." + "Predefinição de Shader salva com sucesso." ) MSG_HASH(MSG_SKIPPING_SRAM_LOAD, "Ignorando carregamento da SRAM." ) MSG_HASH(MSG_SLOW_MOTION, - "Cmera Lenta." + "Câmera Lenta." ) MSG_HASH(MSG_FAST_FORWARD, - "Avano rpido." + "Avanço rápido." ) MSG_HASH(MSG_SLOW_MOTION_REWIND, - "Voltar Atrs em Cmera Lenta." + "Voltar Atrás em Câmera Lenta." ) MSG_HASH(MSG_SRAM_WILL_NOT_BE_SAVED, - "SRAM no ser salva." + "SRAM não será salva." ) MSG_HASH(MSG_STARTING_MOVIE_PLAYBACK, - "Iniciando reproduo de filme." + "Iniciando reprodução de filme." ) MSG_HASH(MSG_STARTING_MOVIE_RECORD_TO, - "Iniciando a gravao de filme em" + "Iniciando a gravação de filme em" ) MSG_HASH(MSG_STATE_SIZE, "Tamanho do Estado de Jogo" @@ -3160,16 +3160,16 @@ MSG_HASH(MSG_UNPAUSED, "Retomando." ) MSG_HASH(MSG_UNRECOGNIZED_COMMAND, - "Comando no reconhecido" + "Comando não reconhecido" ) MSG_HASH(MSG_USING_CORE_NAME_FOR_NEW_CONFIG, - "Usando o nome do ncleo para uma nova configurao." + "Usando o nome do núcleo para uma nova configuração." ) MSG_HASH(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED, - "Usando o ncleo libretro modelo. Pulando a gravao." + "Usando o núcleo libretro modelo. Pulando a gravação." ) MSG_HASH(MSG_VALUE_CONNECT_DEVICE_FROM_A_VALID_PORT, - "Conecte o dispositivo a partir de uma porta vlida." + "Conecte o dispositivo a partir de uma porta válida." ) MSG_HASH(MSG_VALUE_DISCONNECTING_DEVICE_FROM_PORT, "Desconectando o dispositivo da porta" @@ -3181,97 +3181,97 @@ MSG_HASH(MSG_VALUE_SHUTTING_DOWN, "Desligando..." ) MSG_HASH(MSG_VERSION_OF_LIBRETRO_API, - "Verso da API libretro" + "Versão da API libretro" ) MSG_HASH(MSG_VIEWPORT_SIZE_CALCULATION_FAILED, - "Falha no clculo de tamanho da janela de exibio! Prosseguindo usando dados brutos. Isto provavelmente no funcionar corretamente..." + "Falha no cálculo de tamanho da janela de exibição! Prosseguindo usando dados brutos. Isto provavelmente não funcionará corretamente..." ) MSG_HASH(MSG_VIRTUAL_DISK_TRAY, "bandeja de disco virtual." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_LATENCY, - "Latncia de udio desejada em milissegundos. Pode no ser honrado se o driver de udio no puder prover a latncia desejada." + "Latência de áudio desejada em milissegundos. Pode não ser honrado se o driver de áudio não puder prover a latência desejada." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MUTE, - "udio mudo/no-mudo." + "Áudio mudo/não-mudo." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_RATE_CONTROL_DELTA, - "Ajuda a suavizar as imperfeies na regulagem ao sincronizar udio e vdeo. Esteja ciente que se desativado, ser quase impossvel de se obter a sincronia adequada." + "Ajuda a suavizar as imperfeições na regulagem ao sincronizar áudio e vídeo. Esteja ciente que se desativado, será quase impossível de se obter a sincronia adequada." ) MSG_HASH(MENU_ENUM_SUBLABEL_CAMERA_ALLOW, - "Permitir ou no o acesso a cmera pelos ncleos." + "Permitir ou não o acesso a câmera pelos núcleos." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOCATION_ALLOW, - "Permitir ou no o acesso ao servio de localizao pelos ncleos." + "Permitir ou não o acesso ao serviço de localização pelos núcleos." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_MAX_USERS, - "Nmero mximo de usurios suportados pelo RetroArch." + "Número máximo de usuários suportados pelo RetroArch." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_POLL_TYPE_BEHAVIOR, - "Influencia como a chamada seletiva de entrada feita dentro do RetroArch. Definindo com 'Cedo' ou 'Tarde' pode resultar em menos latncia, dependendo da sua configurao." + "Influencia como a chamada seletiva de entrada é feita dentro do RetroArch. Definindo com 'Cedo' ou 'Tarde' pode resultar em menos latência, dependendo da sua configuração." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_ALL_USERS_CONTROL_MENU, - "Permitir a qualquer usurio controlar o menu. Se desabilitado, apenas o Usurio 1 poder controlar o menu." + "Permitir a qualquer usuário controlar o menu. Se desabilitado, apenas o Usuário 1 poderá controlar o menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_VOLUME, - "Volume do udio (em dB). 0dB o volume normal, e nenhum ganho aplicado." + "Volume do áudio (em dB). 0dB é o volume normal, e nenhum ganho é aplicado." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_WASAPI_EXCLUSIVE_MODE, - "Permitir ao driver WASAPI obter controle exclusivo do dispositivo de udio. Se desativado, o modo compartilhado ser utilizado." + "Permitir ao driver WASAPI obter controle exclusivo do dispositivo de áudio. Se desativado, o modo compartilhado será utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_WASAPI_FLOAT_FORMAT, - "Utilizar formato de ponto flutuante para o driver WASAPI, se suportado pelo dispositivo de udio." + "Utilizar formato de ponto flutuante para o driver WASAPI, se suportado pelo dispositivo de áudio." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH, - "O tamanho (em quadros) do buffer intermedirio quando o driver WASAPI estiver em modo compartilhado." + "O tamanho (em quadros) do buffer intermediário quando o driver WASAPI estiver em modo compartilhado." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_SYNC, - "Sincroniza o udio. Recomendado." + "Sincroniza o áudio. Recomendado." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_AXIS_THRESHOLD, - "At que ponto um eixo deve ser movido para resultar em um boto pressionado." + "Até que ponto um eixo deve ser movido para resultar em um botão pressionado." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_BIND_TIMEOUT, - "Quantidade de segundos para aguardar at proceder para o prximo vnculo." + "Quantidade de segundos para aguardar até proceder para o próximo vínculo." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_TURBO_PERIOD, - "Descreve o perodo quando os botes com turbo habilitado so alternados. Os nmeros so descritos em quadros." + "Descreve o período quando os botões com turbo habilitado são alternados. Os números são descritos em quadros." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_DUTY_CYCLE, - "Descreve quo longo deve ser o perodo de um boto com turbo habilitado. Os nmeros so descritos como quadros." + "Descreve quão longo deve ser o período de um botão com turbo habilitado. Os números são descritos como quadros." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VSYNC, - "Sincroniza o vdeo de sada da placa grfica com a taxa de atualizao da tela. Recomendado." + "Sincroniza o vídeo de saída da placa gráfica com a taxa de atualização da tela. Recomendado." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_ALLOW_ROTATE, - "Permite que os ncleos definam a rotao. Quando desabilitado, as requisies de rotao so ignoradas. til para configuraes onde se rotaciona manualmente a tela." + "Permite que os núcleos definam a rotação. Quando desabilitado, as requisições de rotação são ignoradas. Útil para configurações onde se rotaciona manualmente a tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_DUMMY_ON_CORE_SHUTDOWN, - "Alguns ncleos podem ter um recurso de desligamento. Se habilitado, impedir que o ncleo feche o RetroArch. Em vez disto, carrega um ncleo modelo." + "Alguns núcleos podem ter um recurso de desligamento. Se habilitado, impedirá que o núcleo feche o RetroArch. Em vez disto, carrega um núcleo modelo." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHECK_FOR_MISSING_FIRMWARE, - "Verifica se todos os firmwares necessrios esto presentes antes de tentar carregar contedo." + "Verifica se todos os firmwares necessários estão presentes antes de tentar carregar conteúdo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE, - "Taxa de atualizao vertical da sua tela. Utilizado para calcular uma taxa de sada de udio adequada. OBS: Isto ser ignorado se a funo 'Vdeo Paralelizado' estiver habilitada." + "Taxa de atualização vertical da sua tela. Utilizado para calcular uma taxa de saída de áudio adequada. OBS: Isto será ignorado se a função 'Vídeo Paralelizado' estiver habilitada." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_ENABLE, - "Habilita a sada de udio." + "Habilita a saída de áudio." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MAX_TIMING_SKEW, - "Mudana mxima na taxa de entrada de udio. Se aumentado habilita grandes mudanas na regulagem ao custo de impreciso no timbre do som (ex: rodando ncleos PAL em modo NTSC)." + "Mudança máxima na taxa de entrada de áudio. Se aumentado habilita grandes mudanças na regulagem ao custo de imprecisão no timbre do som (ex: rodando núcleos PAL em modo NTSC)." ) MSG_HASH(MSG_FAILED, "falhou" ) MSG_HASH(MSG_SUCCEEDED, - "teve xito" + "teve êxito" ) MSG_HASH(MSG_DEVICE_NOT_CONFIGURED, - "no configurado" + "não configurado" ) MSG_HASH(MSG_DEVICE_NOT_CONFIGURED_FALLBACK, - "no configurado, usando reserva" + "não configurado, usando reserva" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST, "Lista de Cursores da Base de Dados" @@ -3289,7 +3289,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ENABLED, "Habilitado" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_HISTORY_PATH, - "Caminho do Histrico de Contedo" + "Caminho do Histórico de Conteúdo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_ORIGIN, "Base de Dados - Filtro : Origem" @@ -3298,61 +3298,61 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_FRANCHISE, "Base de Dados - Filtro : Franquia" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_ESRB_RATING, - "Base de Dados - Filtro : Classificao ESRB" + "Base de Dados - Filtro : Classificação ESRB" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_ELSPA_RATING, - "Base de Dados - Filtro : Classificao ELSPA" + "Base de Dados - Filtro : Classificação ELSPA" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_PEGI_RATING, - "Base de Dados - Filtro : Classificao PEGI" + "Base de Dados - Filtro : Classificação PEGI" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_CERO_RATING, - "Base de Dados - Filtro : Classificao CERO" + "Base de Dados - Filtro : Classificação CERO" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_BBFC_RATING, - "Base de Dados - Filtro : Classificao BBFC" + "Base de Dados - Filtro : Classificação BBFC" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_MAX_USERS, - "Base de Dados - Filtro : Usurios mximos" + "Base de Dados - Filtro : Usuários máximos" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_RELEASEDATE_BY_MONTH, - "Base de Dados - Filtro : Data de Lanamento Por Ms" + "Base de Dados - Filtro : Data de Lançamento Por Mês" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_RELEASEDATE_BY_YEAR, - "Base de Dados - Filtro : Data de Lanamento Por Ano" + "Base de Dados - Filtro : Data de Lançamento Por Ano" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_EDGE_MAGAZINE_ISSUE, - "Base de Dados - Filtro : Edio da Revista Edge" + "Base de Dados - Filtro : Edição da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_EDGE_MAGAZINE_RATING, - "Base de Dados - Filtro : Classificao da Revista Edge" + "Base de Dados - Filtro : Classificação da Revista Edge" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_DATABASE_INFO, - "Informaes da Base de Dados" + "Informações da Base de Dados" ) MSG_HASH(MSG_WIFI_SCAN_COMPLETE, - "Anlise de Wi-Fi completa." + "Análise de Wi-Fi completa." ) MSG_HASH(MSG_SCANNING_WIRELESS_NETWORKS, "Analisando redes sem fio..." ) MSG_HASH(MSG_NETPLAY_LAN_SCAN_COMPLETE, - "Anlise de Netplay completa." + "Análise de Netplay completa." ) MSG_HASH(MSG_NETPLAY_LAN_SCANNING, "Analisando por hospedeiros de Netplay..." ) MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE, - "Pausar o jogo quando a janela do RetroArch no estiver ativa." + "Pausar o jogo quando a janela do RetroArch não estiver ativa." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION, - "Habilitar ou desabilitar composio (Somente no Windows)." + "Habilitar ou desabilitar composição (Somente no Windows)." ) MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE, - "Habilitar ou desabilitar a lista de reproduo recente para jogos, imagens, msica e vdeos." + "Habilitar ou desabilitar a lista de reprodução recente para jogos, imagens, música e vídeos." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE, - "Limita o nmero de itens da lista de reproduo recente para jogos, imagens, msica e vdeos." + "Limita o número de itens da lista de reprodução recente para jogos, imagens, música e vídeos." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_UNIFIED_MENU_CONTROLS, "Controles de Menu Unificados" @@ -3364,16 +3364,16 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FONT_ENABLE, "Exibir mensagens na tela." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_USER_REMOTE_ENABLE, - "Habilitar Remoto do Usurio %d" + "Habilitar Remoto do Usuário %d" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BATTERY_LEVEL_ENABLE, - "Exibir nvel de bateria" + "Exibir nível de bateria" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SELECT_FILE, "Selecionar Arquivo" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_SELECT_FROM_COLLECTION, - "Selecionar de Coleo" + "Selecionar de Coleção" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FILTER, "Filtro" @@ -3382,10 +3382,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SCALE, "Escala" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_WHEN_LOADED, - "O Netplay ir iniciar quando o contedo for carregado." + "O Netplay irá iniciar quando o conteúdo for carregado." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_LOAD_CONTENT_MANUALLY, - "No foi possvel encontrar um ncleo adequado ou arquivo de contedo, carregue manualmente." + "Não foi possível encontrar um núcleo adequado ou arquivo de conteúdo, carregue manualmente." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_BROWSE_URL_LIST, "Navegar pela URL" @@ -3409,88 +3409,88 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ROOM_NICKNAME_LAN, "Apelido (lan): %s" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND, - "Contedo compatvel encontrado" + "Conteúdo compatível encontrado" ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_CROP_OVERSCAN, - "Corta alguns pixels ao redor das bordas da imagem habitualmente deixada em branco por desenvolvedores, que por vezes tambm contm pixels de lixo." + "Corta alguns pixels ao redor das bordas da imagem habitualmente deixada em branco por desenvolvedores, que por vezes também contêm pixels de lixo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SMOOTH, - "Adiciona um leve embaciado imagem para suavizar as arestas da borda dos pixels. Esta opo tem pouco impacto no desempenho." + "Adiciona um leve embaciado à imagem para suavizar as arestas da borda dos pixels. Esta opção tem pouco impacto no desempenho." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FILTER, - "Aplica um filtro de vdeo processado pela CPU. OBS: Pode vir a um alto custo de desempenho. Alguns filtros de vdeo podem funcionar apenas para ncleos que usam cores de 32 bits ou 16 bits." + "Aplica um filtro de vídeo processado pela CPU. OBS: Pode vir a um alto custo de desempenho. Alguns filtros de vídeo podem funcionar apenas para núcleos que usam cores de 32 bits ou 16 bits." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_USERNAME, - "Insira o nome de usurio de sua conta Retro Achievements." + "Insira o nome de usuário de sua conta Retro Achievements." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_PASSWORD, "Insira a senha de sua conta Retro Achievements." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_NICKNAME, - "Insira seu nome de usurio aqui. Isto ser utilizado para sesses do Netplay, entre outras coisas." + "Insira seu nome de usuário aqui. Isto será utilizado para sessões do Netplay, entre outras coisas." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_POST_FILTER_RECORD, - "Capturar a imagem depois que os filtros (mas no os Shaders) forem aplicados. Seu vdeo ficar to elegante quanto o que voc v na tela." + "Capturar a imagem depois que os filtros (mas não os Shaders) forem aplicados. Seu vídeo ficará tão elegante quanto o que você vê na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_LIST, - "Selecionar qual ncleo utilizar." + "Selecionar qual núcleo utilizar." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOAD_CONTENT_LIST, - "Selecionar qual contedo iniciar." + "Selecionar qual conteúdo iniciar." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETWORK_INFORMATION, - "Exibir interfaces de rede e endereos de IP associados." + "Exibir interfaces de rede e endereços de IP associados." ) MSG_HASH(MENU_ENUM_SUBLABEL_SYSTEM_INFORMATION, - "Exibir informaes especficas do dispositivo." + "Exibir informações específicas do dispositivo." ) MSG_HASH(MENU_ENUM_SUBLABEL_QUIT_RETROARCH, "Sair do programa." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_WINDOW_WIDTH, - "Define a largura personalizada para a janela de exibio. Deixado em 0 a janela ir dimensionar o mais largo possvel." + "Define a largura personalizada para a janela de exibição. Deixado em 0 a janela irá dimensionar o mais largo possível." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_WINDOW_HEIGHT, - "Define a altura personalizada para a janela de exibio. Deixado em 0 a janela ir dimensionar o mais alto possvel." + "Define a altura personalizada para a janela de exibição. Deixado em 0 a janela irá dimensionar o mais alto possível." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FULLSCREEN_X, - "Define a largura personalizada para o modo de tela cheia em no-janela. Deixar em 0 ir usar a resoluo da rea de trabalho." + "Define a largura personalizada para o modo de tela cheia em não-janela. Deixar em 0 irá usar a resolução da área de trabalho." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FULLSCREEN_Y, - "Define a altura personalizada para o modo de tela cheia em no-janela. Deixar em 0 ir usar a resoluo da rea de trabalho." + "Define a altura personalizada para o modo de tela cheia em não-janela. Deixar em 0 irá usar a resolução da área de trabalho." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MESSAGE_POS_X, - "Especifique a posio personalizada no eixo X para o texto na tela." + "Especifique a posição personalizada no eixo X para o texto na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MESSAGE_POS_Y, - "Especifique a posio personalizada no eixo Y para o texto na tela." + "Especifique a posição personalizada no eixo Y para o texto na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FONT_SIZE, "Especifique o tamanho da fonte em pontos." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_HIDE_IN_MENU, - "Ocultar a Transparncia enquanto estiver dentro do menu e exibir novamente ao sair." + "Ocultar a Transparência enquanto estiver dentro do menu e exibir novamente ao sair." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS, - "Exibir comandos de teclado/controle na transparncia." + "Exibir comandos de teclado/controle na transparência." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS_PORT, - "Selecione a porta para a transparncia escutar se Exibir Comandos na Transparncia estiver habilitado." + "Selecione a porta para a transparência escutar se Exibir Comandos na Transparência estiver habilitado." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_COLLECTION_LIST, - "O contedo analisado aparecer aqui." + "O conteúdo analisado aparecerá aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SCALE_INTEGER, - "Apenas dimensiona o vdeo em valores inteiros. O tamanho de base depende da geometria relatada pelo sistema e da proporo de tela. Se 'Forar Proporo' no estiver definido, X / Y sero dimensionados independentemente em valores inteiros." + "Apenas dimensiona o vídeo em valores inteiros. O tamanho de base depende da geometria relatada pelo sistema e da proporção de tela. Se 'Forçar Proporção' não estiver definido, X / Y serão dimensionados independentemente em valores inteiros." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_GPU_SCREENSHOT, - "Captura a tela com Shader de GPU se disponvel." + "Captura a tela com Shader de GPU se disponível." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_ROTATION, - "Fora uma certa rotao da tela. A rotao adicionada a rotao que o ncleo definir." + "Força uma certa rotação da tela. A rotação é adicionada a rotação que o núcleo definir." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FORCE_SRGB_DISABLE, - "Desabilita de forma forada o suporte sRGB FBO. Alguns drivers Intel OpenGL no Windows possuem problemas de vdeo com o suporte sRGB FBO se estiver habilitado. Habilitando isto pode contornar o problema." + "Desabilita de forma forçada o suporte sRGB FBO. Alguns drivers Intel OpenGL no Windows possuem problemas de vídeo com o suporte sRGB FBO se estiver habilitado. Habilitando isto pode contornar o problema." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FULLSCREEN, "Inicia em tela cheia. Pode ser mudado a qualquer momento." @@ -3499,58 +3499,58 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_WINDOWED_FULLSCREEN, "Se estiver em tela cheia, prefira utilizar uma janela de tela cheia." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_GPU_RECORD, - "Grava o material de sada do Shader de GPU se disponvel." + "Grava o material de saída do Shader de GPU se disponível." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_INDEX, - "Ao criar um Estado de Jogo, o ndice do Estado de Jogo aumentado automaticamente antes de ser salvo. Ao carregar um contedo, o ndice ser definido para o ndice mais alto existente." + "Ao criar um Estado de Jogo, o índice do Estado de Jogo é aumentado automaticamente antes de ser salvo. Ao carregar um conteúdo, o índice será definido para o índice mais alto existente." ) MSG_HASH(MENU_ENUM_SUBLABEL_BLOCK_SRAM_OVERWRITE, "Bloqueia a SRAM de ser sobrescrita ao carregar um Estado de Jogo. Pode causar problemas no jogo." ) MSG_HASH(MENU_ENUM_SUBLABEL_FASTFORWARD_RATIO, - "A taxa mxima na qual o contedo ser executado quando utilizado o Avano Rpido (ex: 5.0x para contedos em 60fps = 300 fps max). Se for definido como 0.0x, a taxa de Avano Rpido ilimitada (sem FPS max)." + "A taxa máxima na qual o conteúdo será executado quando utilizado o Avanço Rápido (ex: 5.0x para conteúdos em 60fps = 300 fps max). Se for definido como 0.0x, a taxa de Avanço Rápido é ilimitada (sem FPS max)." ) MSG_HASH(MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO, - "Quando est em Cmera Lenta, o contedo ser diminudo pelo fator especificado/definido." + "Quando está em Câmera Lenta, o conteúdo será diminuído pelo fator especificado/definido." ) MSG_HASH(MENU_ENUM_SUBLABEL_REWIND_ENABLE, - "Habilita Voltar Atrs. Isso ir impactar o desempenho ao jogar." + "Habilita Voltar Atrás. Isso irá impactar o desempenho ao jogar." ) MSG_HASH(MENU_ENUM_SUBLABEL_REWIND_GRANULARITY, - "Ao definir um nmero de quadros para Voltar Atrs, voc pode retroceder vrios quadros de uma s vez, aumentando a velocidade da funo." + "Ao definir um número de quadros para Voltar Atrás, você pode retroceder vários quadros de uma só vez, aumentando a velocidade da função." ) MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_LOG_LEVEL, - "Define o nvel de registro de eventos para os ncleos. Se o nvel do registro enviado por um ncleo for abaixo deste valor, o mesmo ignorado." + "Define o nível de registro de eventos para os núcleos. Se o nível do registro enviado por um núcleo for abaixo deste valor, o mesmo é ignorado." ) MSG_HASH(MENU_ENUM_SUBLABEL_PERFCNT_ENABLE, - "Habilitar os contadores de desempenho para o RetroArch (e ncleos)." + "Habilitar os contadores de desempenho para o RetroArch (e núcleos)." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_SAVE, - "Cria automaticamente um Estado de Jogo no final da execuo do RetroArch. O RetroArch ir carregar automaticamente este Estado de Jogo se a funo 'Autocarregar Estado de Jogo' estiver habilitada." + "Cria automaticamente um Estado de Jogo no final da execução do RetroArch. O RetroArch irá carregar automaticamente este Estado de Jogo se a função 'Autocarregar Estado de Jogo' estiver habilitada." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_LOAD, - "Autocarrega o ltimo Estado de Jogo autosalvo na inicializao do RetroArch." + "Autocarrega o último Estado de Jogo autosalvo na inicialização do RetroArch." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_DRIVER, - "Driver de reamostragem de udio a ser utilizado." + "Driver de reamostragem de áudio a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_CAMERA_DRIVER, - "Driver de cmera a ser utilizado." + "Driver de câmera a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOCATION_DRIVER, - "Driver de localizao a ser utilizado." + "Driver de localização a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_DRIVER, "Driver de menu a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_RECORD_DRIVER, - "Driver de gravao a ser utilizado." + "Driver de gravação a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_DRIVER, "Driver de WiFi a ser utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE, - "Filtra os arquivos em exibio no explorador de arquivos por extenses suportadas." + "Filtra os arquivos em exibição no explorador de arquivos por extensões suportadas." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_WALLPAPER, "Seleciona uma imagem para definir como plano de fundo do menu." @@ -3559,58 +3559,58 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DYNAMIC_WALLPAPER, "Carrega dinamicamente um novo plano de fundo dependendo do contexto." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_DEVICE, - "Substitui o dispositivo de udio padro utilizado pelo driver de udio. Isto depende do driver." + "Substitui o dispositivo de áudio padrão utilizado pelo driver de áudio. Isto depende do driver." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_DSP_PLUGIN, - "Plugin DSP de udio que processa o udio antes de ser enviado para o driver." + "Plugin DSP de Áudio que processa o áudio antes de ser enviado para o driver." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_RATE, - "Taxa de amostragem da sada de udio." + "Taxa de amostragem da saída de áudio." ) MSG_HASH(MENU_ENUM_SUBLABEL_OVERLAY_OPACITY, - "Opacidade de todos os elementos de interface da Transparncia." + "Opacidade de todos os elementos de interface da Transparência." ) MSG_HASH(MENU_ENUM_SUBLABEL_OVERLAY_SCALE, - "Escala de todos os elementos de interface da Transparncia." + "Escala de todos os elementos de interface da Transparência." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_ENABLE, - "Habilita a Transparncia." + "Habilita a Transparência." ) MSG_HASH(MENU_ENUM_SUBLABEL_OVERLAY_PRESET, - "Seleciona uma Transparncia pelo navegador de arquivos." + "Seleciona uma Transparência pelo navegador de arquivos." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_IP_ADDRESS, - "Endereo do hospedeiro a se conectar." + "Endereço do hospedeiro a se conectar." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_TCP_UDP_PORT, - "Porta do endereo de IP do hospedeiro. Pode ser uma porta TCP ou uma porta UDP." + "Porta do endereço de IP do hospedeiro. Pode ser uma porta TCP ou uma porta UDP." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_PASSWORD, "Senha para conectar ao hospedeiro de Netplay. Utilizado apenas no modo hospedeiro." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_PUBLIC_ANNOUNCE, - "Anunciar os jogos de Netplay publicamente. Se no for definido, os clientes devero conectar manualmente em vez de usar o lobby pblico." + "Anunciar os jogos de Netplay publicamente. Se não for definido, os clientes deverão conectar manualmente em vez de usar o lobby público." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_SPECTATE_PASSWORD, - "Senha para conectar ao hospedeiro de Netplay apenas com privilgios de espectador. Utilizado apenas no modo hospedeiro." + "Senha para conectar ao hospedeiro de Netplay apenas com privilégios de espectador. Utilizado apenas no modo hospedeiro." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_START_AS_SPECTATOR, "Define se o Netplay deve iniciar em modo espectador." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_ALLOW_SLAVES, - "Define se conexes em modo escravo so permitidas. Clientes em modo escravo requerem muito pouco poder de processamento em ambos os lados, mas iro sofrer significamente da latncia de rede." + "Define se conexões em modo escravo são permitidas. Clientes em modo escravo requerem muito pouco poder de processamento em ambos os lados, mas irão sofrer significamente da latência de rede." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REQUIRE_SLAVES, - "Define se conexes que no esto em modo escravo so proibidas. No recomendado, exceto para redes muito rpidas com mquinas muito lentas." + "Define se conexões que não estão em modo escravo são proibidas. Não recomendado, exceto para redes muito rápidas com máquinas muito lentas." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_STATELESS_MODE, - "Define se deve executar o Netplay em modo que no utilize Estados de Jogo. Se definido como verdadeiro, uma rede muito rpida necessria, mas Voltar Atrs no permitido, ento no haver oscilao no Netplay." + "Define se deve executar o Netplay em modo que não utilize Estados de Jogo. Se definido como verdadeiro, uma rede muito rápida é necessária, mas Voltar Atrás não é permitido, então não haverá oscilação no Netplay." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CHECK_FRAMES, - "Frequncia em quadros no qual o Netplay verificar se o hospedeiro e o cliente esto sincronizados." + "Frequência em quadros no qual o Netplay verificará se o hospedeiro e o cliente estão sincronizados." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_NAT_TRAVERSAL, - "Ao hospedar uma partida, tente receber conexes da Internet pblica usando UPnP ou tecnologias similares para escapar das redes locais." + "Ao hospedar uma partida, tente receber conexões da Internet pública usando UPnP ou tecnologias similares para escapar das redes locais." ) MSG_HASH(MENU_ENUM_SUBLABEL_STDIN_CMD_ENABLE, "Habilitar interface de comando stdin." @@ -3626,16 +3626,16 @@ MSG_HASH(MENU_ENUM_SUBLABEL_THUMBNAILS, ) MSG_HASH( MENU_ENUM_SUBLABEL_LEFT_THUMBNAILS, - "Tipo de miniatura para exibir esquerda." + "Tipo de miniatura para exibir à esquerda." ) MSG_HASH(MENU_ENUM_SUBLABEL_TIMEDATE_ENABLE, "Exibir data e/ou hora atuais dentro do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_BATTERY_LEVEL_ENABLE, - "Exibir o nvel de bateria atual dentro do menu." + "Exibir o nível de bateria atual dentro do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_NAVIGATION_WRAPAROUND, - "Voltar ao incio ou final se o limite da lista for alcanado horizontalmente ou verticalmente." + "Voltar ao início ou final se o limite da lista for alcançado horizontalmente ou verticalmente." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_ENABLE_HOST, "Habilita o Netplay no modo hospedeiro (servidor)." @@ -3644,42 +3644,42 @@ MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_ENABLE_CLIENT, "Habilita o Netplay no modo cliente." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_DISCONNECT, - "Desconecta de uma conexo de Netplay ativa." + "Desconecta de uma conexão de Netplay ativa." ) MSG_HASH(MENU_ENUM_SUBLABEL_SCAN_DIRECTORY, - "Analisa um diretrio por arquivos compatveis e os adiciona coleo." + "Analisa um diretório por arquivos compatíveis e os adiciona à coleção." ) MSG_HASH(MENU_ENUM_SUBLABEL_SCAN_FILE, - "Analisa um arquivo compatvel e o adiciona coleo." + "Analisa um arquivo compatível e o adiciona à coleção." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SWAP_INTERVAL, - "Usa um intervalo de troca personalizado para V-Sync. Defina para reduzir efetivamente a taxa de atualizao do monitor pela metade." + "Usa um intervalo de troca personalizado para V-Sync. Defina para reduzir efetivamente a taxa de atualização do monitor pela metade." ) MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, - "Ordenar os Jogos-Salvos em pastas com o nome do ncleo utilizado." + "Ordenar os Jogos-Salvos em pastas com o nome do núcleo utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, - "Ordenar os Estados de Jogo em pastas com o nome do ncleo utilizado." + "Ordenar os Estados de Jogo em pastas com o nome do núcleo utilizado." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REQUEST_DEVICE_I, "Solicitar jogar com o dispositivo de entrada dado.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, - "URL para o diretrio de atualizao de ncleos no buildbot do Libreto." + "URL para o diretório de atualização de núcleos no buildbot do Libreto." ) MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, - "URL para o diretrio de atualizaes de recursos no buildbot do Libretro." + "URL para o diretório de atualizações de recursos no buildbot do Libretro." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE, - "Aps o download, extrair automaticamente os arquivos contidos nos arquivos comprimidos baixados." + "Após o download, extrair automaticamente os arquivos contidos nos arquivos comprimidos baixados." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REFRESH_ROOMS, "Analisar por novas salas." ) MSG_HASH(MENU_ENUM_SUBLABEL_DELETE_ENTRY, - "Remover esta entrada da coleo." + "Remover esta entrada da coleção." ) MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION, - "Visualizar mais informaes sobre o contedo." + "Visualizar mais informações sobre o conteúdo." ) MSG_HASH(MENU_ENUM_SUBLABEL_ADD_TO_FAVORITES, "Adicionar o item aos seus favoritos." @@ -3688,22 +3688,22 @@ MSG_HASH(MENU_ENUM_SUBLABEL_ADD_TO_FAVORITES_PLAYLIST, "Adicionar o item aos seus favoritos." ) MSG_HASH(MENU_ENUM_SUBLABEL_RUN, - "Iniciar o contedo." + "Iniciar o conteúdo." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_FILE_BROWSER_SETTINGS, - "Ajustar as definies do navegador de arquivos." + "Ajustar as definições do navegador de arquivos." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUTO_REMAPS_ENABLE, - "Habilitar por padro controles personalizados na inicializao." + "Habilitar por padrão controles personalizados na inicialização." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUTO_OVERRIDES_ENABLE, - "Habilitar por padro configurao personalizada na inicializao." + "Habilitar por padrão configuração personalizada na inicialização." ) MSG_HASH(MENU_ENUM_SUBLABEL_GAME_SPECIFIC_OPTIONS, - "Habilitar por padro opes de ncleo personalizadas na inicializao." + "Habilitar por padrão opções de núcleo personalizadas na inicialização." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_ENABLE, - "Exibir o nome do ncleo atual dentro do menu." + "Exibir o nome do núcleo atual dentro do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_DATABASE_MANAGER, "Visualizar bases de dados." @@ -3715,7 +3715,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_TAKE_SCREENSHOT, "Capturar uma imagem da tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_CLOSE_CONTENT, - "Fecha o contedo atual. Alteraes no salvas sero perdidas." + "Fecha o conteúdo atual. Alterações não salvas serão perdidas." ) MSG_HASH(MENU_ENUM_SUBLABEL_LOAD_STATE, "Carregar um Estado de Jogo do compartimento selecionado atualmente." @@ -3724,88 +3724,88 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_STATE, "Salvar um Estado de Jogo no compartimento selecionado atualmente." ) MSG_HASH(MENU_ENUM_SUBLABEL_RESUME, - "Retomar a execuo do contedo atual e sair do Menu Rpido." + "Retomar a execução do conteúdo atual e sair do Menu Rápido." ) MSG_HASH(MENU_ENUM_SUBLABEL_RESUME_CONTENT, - "Retomar a execuo do contedo atual e sair do Menu Rpido." + "Retomar a execução do conteúdo atual e sair do Menu Rápido." ) MSG_HASH(MENU_ENUM_SUBLABEL_STATE_SLOT, "Altera o compartimento do Estado de Jogo selecionado atualmente." ) MSG_HASH(MENU_ENUM_SUBLABEL_UNDO_LOAD_STATE, - "Se um Estado de Jogo for carregado, o contedo voltar ao estado anterior ao carregamento." + "Se um Estado de Jogo for carregado, o conteúdo voltará ao estado anterior ao carregamento." ) MSG_HASH(MENU_ENUM_SUBLABEL_UNDO_SAVE_STATE, - "Se o Estado de Jogo foi sobrescrito, ele voltar ao Estado de Jogo salvo anteriormente." + "Se o Estado de Jogo foi sobrescrito, ele voltará ao Estado de Jogo salvo anteriormente." ) MSG_HASH(MENU_ENUM_SUBLABEL_ACCOUNTS_RETRO_ACHIEVEMENTS, - "Servio Retro Achievements. Para mais informaes, visite http://retroachievements.org (em ingls)" + "Serviço Retro Achievements. Para mais informações, visite http://retroachievements.org (em inglês)" ) MSG_HASH(MENU_ENUM_SUBLABEL_ACCOUNTS_LIST, "Gerenciar contas configuradas atualmente." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_META_REWIND, - "Gerenciar as configuraes de Voltar Atrs." + "Gerenciar as configurações de Voltar Atrás." ) MSG_HASH(MENU_ENUM_SUBLABEL_RESTART_CONTENT, - "Reinicia o contedo do comeo." + "Reinicia o conteúdo do começo." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE, - "Salva um arquivo de redefinio de configurao que ser aplicado a todo o contedo carregado por este ncleo. Ter prioridade sobre a configurao principal." + "Salva um arquivo de redefinição de configuração que será aplicado a todo o conteúdo carregado por este núcleo. Terá prioridade sobre a configuração principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_CURRENT_CONFIG_OVERRIDE_GAME, - "Salva um arquivo de redefinio de configurao que ser aplicado apenas ao contedo atual. Ter prioridade sobre a configurao principal." + "Salva um arquivo de redefinição de configuração que será aplicado apenas ao conteúdo atual. Terá prioridade sobre a configuração principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_CHEAT_OPTIONS, - "Configurar cdigos de Trapaa." + "Configurar códigos de Trapaça." ) MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_OPTIONS, - "Configurar Shader para realar a aparncia da imagem." + "Configurar Shader para realçar a aparência da imagem." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_INPUT_REMAPPING_OPTIONS, - "Alterar os controles para o contedo que est sendo executado." + "Alterar os controles para o conteúdo que está sendo executado." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_OPTIONS, - "Alterar as opes para o contedo que est sendo executado." + "Alterar as opções para o conteúdo que está sendo executado." ) MSG_HASH(MENU_ENUM_SUBLABEL_SHOW_ADVANCED_SETTINGS, - "Exibir as configuraes avanadas para usurios experientes (oculto por padro)." + "Exibir as configurações avançadas para usuários experientes (oculto por padrão)." ) MSG_HASH(MENU_ENUM_SUBLABEL_THREADED_DATA_RUNLOOP_ENABLE, "Executar tarefas em linhas de processamento paralelas." ) MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_ENTRY_REMOVE, - "Permitir que o usurio possa remover itens das colees." + "Permitir que o usuário possa remover itens das coleções." ) MSG_HASH(MENU_ENUM_SUBLABEL_SYSTEM_DIRECTORY, - "Define o diretrio de sistema. Os ncleos podem consultar este diretrio para carregar arquivos de BIOS, configuraes especficas do sistema, etc." + "Define o diretório de sistema. Os núcleos podem consultar este diretório para carregar arquivos de BIOS, configurações específicas do sistema, etc." ) MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_BROWSER_DIRECTORY, - "Define o diretrio inicial do navegador de arquivos." + "Define o diretório inicial do navegador de arquivos." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_DIR, "Usualmente definido por desenvolvedores que agrupam aplicativos Libretro/RetroArch para apontar para os recursos." ) MSG_HASH(MENU_ENUM_SUBLABEL_DYNAMIC_WALLPAPERS_DIRECTORY, - "Diretrio para armazenar planos de fundo dinamicamente carregados pelo menu dependendo do contexto." + "Diretório para armazenar planos de fundo dinamicamente carregados pelo menu dependendo do contexto." ) MSG_HASH(MENU_ENUM_SUBLABEL_THUMBNAILS_DIRECTORY, - "Miniaturas auxiliares (arte da embalagem/imagens diversas e etc.) so armazenadas aqui." + "Miniaturas auxiliares (arte da embalagem/imagens diversas e etc.) são armazenadas aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_CONFIG_DIRECTORY, - "Define o diretrio inicial para o navegador de configuraes do menu." + "Define o diretório inicial para o navegador de configurações do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN, - "O nmero de quadros de latncia de entrada para o Netplay utilizar para mascarar a latncia da rede. Reduz a oscilao e torna o Netplay menos intensivo para a CPU, ao custo de atraso perceptvel na entrada." + "O número de quadros de latência de entrada para o Netplay utilizar para mascarar a latência da rede. Reduz a oscilação e torna o Netplay menos intensivo para a CPU, ao custo de atraso perceptível na entrada." ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, - "O intervalo de quadros de latncia de entrada que pode ser utilizado para mascarar a latncia da rede. Reduz a oscilao e torna o Netplay menos intensivo para a CPU, ao custo de atraso imprevisvel na entrada." + "O intervalo de quadros de latência de entrada que pode ser utilizado para mascarar a latência da rede. Reduz a oscilação e torna o Netplay menos intensivo para a CPU, ao custo de atraso imprevisível na entrada." ) MSG_HASH(MENU_ENUM_SUBLABEL_DISK_CYCLE_TRAY_STATUS, - "Alterna o disco atual. Se o disco estiver inserido, o mesmo ser ejetado. Se o disco no estiver inserido, o mesmo ser inserido." + "Alterna o disco atual. Se o disco estiver inserido, o mesmo será ejetado. Se o disco não estiver inserido, o mesmo será inserido." ) MSG_HASH(MENU_ENUM_SUBLABEL_DISK_INDEX, - "Mudar o ndice do disco." + "Mudar o índice do disco." ) MSG_HASH(MENU_ENUM_SUBLABEL_DISK_OPTIONS, "Gerenciamento de imagem de disco." @@ -3814,13 +3814,13 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Selecione uma imagem de disco para inserir." ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, - "Certifica-se de que a taxa de quadros controlada enquanto estiver dentro do menu." + "Certifica-se de que a taxa de quadros é controlada enquanto estiver dentro do menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, - "Selecionar um tema diferente para os cones. As alteraes tero efeito aps reiniciar o programa." + "Selecionar um tema diferente para os ícones. As alterações terão efeito após reiniciar o programa." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, - "Habilitar as sombras para todos os cones. Isto ter um pequeno impacto no desempenho." + "Habilitar as sombras para todos os ícones. Isto terá um pequeno impacto no desempenho." ) MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_COLOR_THEME, "Selecionar um tema de gradiente de cor de plano de fundo diferente." @@ -3832,7 +3832,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_XMB_MENU_COLOR_THEME, "Selecionar um tema de gradiente de cor de plano de fundo diferente." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_RIBBON_ENABLE, - "Selecionar um efeito de plano de fundo animado. Pode exigir mais processamento da GPU dependendo do efeito. Se o desempenho for insatisfatrio, desligue este efeito ou reverta para um efeito mais simples." + "Selecionar um efeito de plano de fundo animado. Pode exigir mais processamento da GPU dependendo do efeito. Se o desempenho for insatisfatório, desligue este efeito ou reverta para um efeito mais simples." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_FONT, "Selecionar uma fonte principal diferente para ser usada pelo menu." @@ -3844,167 +3844,167 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_IMAGES, "Exibir a aba de imagem dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_MUSIC, - "Exibir a aba de msica dentro do menu principal." + "Exibir a aba de música dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_VIDEO, - "Exibir a aba de vdeo dentro do menu principal." + "Exibir a aba de vídeo dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_NETPLAY, "Exibir a aba de Netplay dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_SETTINGS, - "Mostrar a aba de configuraes dentro do menu principal." + "Mostrar a aba de configurações dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, - "Mostrar a aba de histrico recente dentro do menu principal." + "Mostrar a aba de histórico recente dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, - "Mostrar a aba de importao de contedo dentro do menu principal." + "Mostrar a aba de importação de conteúdo dentro do menu principal." ) MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, - "Exibir a tela inicial no menu. automaticamente definido como falso aps o programa iniciar pela primeira vez." + "Exibir a tela inicial no menu. É automaticamente definido como falso após o programa iniciar pela primeira vez." ) MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, - "Modificar a opacidade do grfico do cabealho." + "Modificar a opacidade do gráfico do cabeçalho." ) MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_FOOTER_OPACITY, - "Modificar a opacidade do grfico do rodap." + "Modificar a opacidade do gráfico do rodapé." ) MSG_HASH(MENU_ENUM_SUBLABEL_DPI_OVERRIDE_ENABLE, - "O menu normalmente se dimensiona dinamicamente. Se voc desejar definir uma escala de tamanho especfica em vez disto, habilite esta funo." + "O menu normalmente se dimensiona dinamicamente. Se você desejar definir uma escala de tamanho específica em vez disto, habilite esta função." ) MSG_HASH(MENU_ENUM_SUBLABEL_DPI_OVERRIDE_VALUE, - "Definir o tamanho do dimensionamento personalizado aqui. OBS: Voc deve habilitar a funo 'Redefinio de DPI' para que este dimensionamento tenha efeito." + "Definir o tamanho do dimensionamento personalizado aqui. OBS: Você deve habilitar a função 'Redefinição de DPI' para que este dimensionamento tenha efeito." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_ASSETS_DIRECTORY, - "Salvar todos os arquivos baixados neste diretrio." + "Salvar todos os arquivos baixados neste diretório." ) MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_REMAPPING_DIRECTORY, - "Salvar todos os controles remapeados neste diretrio." + "Salvar todos os controles remapeados neste diretório." ) MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_DIR_PATH, - "Diretrio onde o programa busca por contedo/ncleos." + "Diretório onde o programa busca por conteúdo/núcleos." ) MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_INFO_PATH, - "Os arquivos de informao do aplicativo/ncleo so armazenados aqui." + "Os arquivos de informação do aplicativo/núcleo são armazenados aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_JOYPAD_AUTOCONFIG_DIR, - "Se um Joypad estiver conectado, o mesmo ser autoconfigurado se um arquivo de configurao correspondente estiver presente dento deste diretrio." + "Se um Joypad estiver conectado, o mesmo será autoconfigurado se um arquivo de configuração correspondente estiver presente dento deste diretório." ) MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_DIRECTORY, - "Salvar todas as colees neste diretrio." + "Salvar todas as coleções neste diretório." ) MSG_HASH(MENU_ENUM_SUBLABEL_CACHE_DIRECTORY, - "Se for definido um diretrio, o contedo que temporariamente extrado (ex: dos arquivos) sera extrado para este diretrio." + "Se for definido um diretório, o conteúdo que é temporariamente extraído (ex: dos arquivos) sera extraído para este diretório." ) MSG_HASH(MENU_ENUM_SUBLABEL_CURSOR_DIRECTORY, - "As consultas salvas so armazenadas neste diretrio." + "As consultas salvas são armazenadas neste diretório." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_DATABASE_DIRECTORY, - "As bases de dados so armazenadas neste diretrio." + "As bases de dados são armazenadas neste diretório." ) MSG_HASH(MENU_ENUM_SUBLABEL_ASSETS_DIRECTORY, - "Esta localizao consultada por padro quando a interface do menu tenta procurar por recursos carregveis, etc." + "Esta localização é consultada por padrão quando a interface do menu tenta procurar por recursos carregáveis, etc." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVEFILE_DIRECTORY, - "Salvar todos os Jogos-Salvos neste diretrio. Se no for definido, tentaremos salvar dentro do diretrio de trabalho do arquivo." + "Salvar todos os Jogos-Salvos neste diretório. Se não for definido, tentaremos salvar dentro do diretório de trabalho do arquivo." ) MSG_HASH(MENU_ENUM_SUBLABEL_SAVESTATE_DIRECTORY, - "Salvar todos os Estados de Jogo neste diretrio. Se no for definido, tentaremos salvar dentro do diretrio de trabalho do arquivo." + "Salvar todos os Estados de Jogo neste diretório. Se não for definido, tentaremos salvar dentro do diretório de trabalho do arquivo." ) MSG_HASH(MENU_ENUM_SUBLABEL_SCREENSHOT_DIRECTORY, - "Diretrio para armazenar as capturas de tela." + "Diretório para armazenar as capturas de tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_OVERLAY_DIRECTORY, - "Define um diretrio onde as Transparncias so mantidas para fcil acesso." + "Define um diretório onde as Transparências são mantidas para fácil acesso." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_DATABASE_PATH, - "Os arquivos de Trapaa so mantidos aqui." + "Os arquivos de Trapaça são mantidos aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_FILTER_DIR, - "Diretrio onde os arquivos de filtro DSP de udio so mantidos." + "Diretório onde os arquivos de filtro DSP de áudio são mantidos." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FILTER_DIR, - "Diretrio onde os arquivos de filtro de vdeo processado por CPU so mantidos." + "Diretório onde os arquivos de filtro de vídeo processado por CPU são mantidos." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_DIR, - "Define um diretrio onde os arquivos de Shader de vdeo processado por GPU so mantidos para fcil acesso." + "Define um diretório onde os arquivos de Shader de vídeo processado por GPU são mantidos para fácil acesso." ) MSG_HASH(MENU_ENUM_SUBLABEL_RECORDING_OUTPUT_DIRECTORY, - "As gravaes sero armazenadas neste diretrio." + "As gravações serão armazenadas neste diretório." ) MSG_HASH(MENU_ENUM_SUBLABEL_RECORDING_CONFIG_DIRECTORY, - "As configuraes de gravao sero mantidas aqui." + "As configurações de gravação serão mantidas aqui." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FONT_PATH, - "Selecionar uma fonte diferente para as notificaes na tela." + "Selecionar uma fonte diferente para as notificações na tela." ) MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_APPLY_CHANGES, - "As alteraes das configuraes de Shader tero efeito imediato. Use isto se voc alterou a quantidade de estgios de Shader, filtros, escala FBO, etc." + "As alterações das configurações de Shader terão efeito imediato. Use isto se você alterou a quantidade de estágios de Shader, filtros, escala FBO, etc." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_NUM_PASSES, - "Aumentar ou diminuir a quantidade de estgios do pipeline de Shader. Voc pode adicionar um Shader separado para cada estgio do pipeline e configurar sua escala e filtro." + "Aumentar ou diminuir a quantidade de estágios do pipeline de Shader. Você pode adicionar um Shader separado para cada estágio do pipeline e configurar sua escala e filtro." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET, - "Carregar uma predefinio de Shader. O pipeline de Shader ser definido automaticamente." + "Carregar uma predefinição de Shader. O pipeline de Shader será definido automaticamente." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_AS, - "Salvar as definies de Shader atuais como uma nova predefinio de Shader." + "Salvar as definições de Shader atuais como uma nova predefinição de Shader." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_CORE, - "Salvar as definies de Shader atuais como a definio padro para esta aplicao/ncleo." + "Salvar as definições de Shader atuais como a definição padrão para esta aplicação/núcleo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_GAME, - "Salvar as definies de Shader atuais como a definio padro para o contedo." + "Salvar as definições de Shader atuais como a definição padrão para o conteúdo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PARAMETERS, - "Modifica diretamente o Shader atual. As alteraes no sero salvas no arquivo de predefinio." + "Modifica diretamente o Shader atual. As alterações não serão salvas no arquivo de predefinição." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_PARAMETERS, - "Modifica a predefinio de Shader atualmente utilizada no menu." + "Modifica a predefinição de Shader atualmente utilizada no menu." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_NUM_PASSES, - "Aumentar ou diminuir a quantidade de Trapaas." + "Aumentar ou diminuir a quantidade de Trapaças." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_APPLY_CHANGES, - "As alteraes de Trapaa tero efeito imediato." + "As alterações de Trapaça terão efeito imediato." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_FILE_LOAD, - "Carregar um arquivo de Trapaa." + "Carregar um arquivo de Trapaça." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEAT_FILE_SAVE_AS, - "Salvar as Trapaas atuais como um arquivo de Jogo-Salvo." + "Salvar as Trapaças atuais como um arquivo de Jogo-Salvo." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SETTINGS, - "Acessar rapidamente todas as configuraes relevantes ao jogo." + "Acessar rapidamente todas as configurações relevantes ao jogo." ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_INFORMATION, - "Visualizar informaes sobre a aplicao/ncleo." + "Visualizar informações sobre a aplicação/núcleo." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_ASPECT_RATIO, - "Valor em ponto flutuante da proporo de tela (largura / altura), utilizado se Proporo de Tela estiver definido como 'Configurao'." + "Valor em ponto flutuante da proporção de tela (largura / altura), utilizado se Proporção de Tela estiver definido como 'Configuração'." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_HEIGHT, - "Personalizar a altura da janela de exibio que usada se a Proporo de Tela estiver definida como 'Personalizada'." + "Personalizar a altura da janela de exibição que é usada se a Proporção de Tela estiver definida como 'Personalizada'." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_WIDTH, - "Personalizar a largura da janela de exibio que usada se a Proporo de Tela estiver definida como 'Personalizada'." + "Personalizar a largura da janela de exibição que é usada se a Proporção de Tela estiver definida como 'Personalizada'." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_X, - "Deslocamento personalizado no eixo-X da janela de exibio. Ser ignorado se a 'Escala em Inteiros' estiver habilitada. Neste caso ela ser centralizada automaticamente." + "Deslocamento personalizado no eixo-X da janela de exibição. Será ignorado se a 'Escala em Inteiros' estiver habilitada. Neste caso ela será centralizada automaticamente." ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_Y, - "Deslocamento personalizado no eixo-Y da janela de exibio. Ser ignorado se a 'Escala em Inteiros' estiver habilitada. Neste caso ela ser centralizada automaticamente." + "Deslocamento personalizado no eixo-Y da janela de exibição. Será ignorado se a 'Escala em Inteiros' estiver habilitada. Neste caso ela será centralizada automaticamente." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_USE_MITM_SERVER, "Utilizar Servidor MITM" ) MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_USE_MITM_SERVER, - "Encaminhar conexes do Netplay atravs de um servidor 'homem no meio' (MITM). til se o hospedeiro estiver atrs de um firewall ou tiver problemas de NAT/UPnP.") + "Encaminhar conexões do Netplay através de um servidor 'homem no meio' (MITM). Útil se o hospedeiro estiver atrás de um firewall ou tiver problemas de NAT/UPnP.") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_MITM_SERVER, - "Localizao do Servidor de Retransmisso") + "Localização do Servidor de Retransmissão") MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_MITM_SERVER, - "Escolha um servidor de retransmisso especfico para usar. Locais geograficamente mais prximos tendem a ter menor latncia.") + "Escolha um servidor de retransmissão específico para usar. Locais geograficamente mais próximos tendem a ter menor latência.") MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_MIXER, "Adicionar ao mixer" ) @@ -4012,49 +4012,49 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_ADD_TO_MIXER_AND_COLLECTION, "Adicionar ao mixer" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_FILTER_BY_CURRENT_CORE, - "Filtrar por ncleo atual" + "Filtrar por núcleo atual" ) MSG_HASH(MSG_AUDIO_MIXER_VOLUME, - "Volume global do mixer de udio" + "Volume global do mixer de áudio" ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MIXER_VOLUME, - "Volume global do mixer de udio (em dB). 0dB o volume normal, e nenhum ganho ser aplicado." + "Volume global do mixer de áudio (em dB). 0dB é o volume normal, e nenhum ganho será aplicado." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_VOLUME, - "Nvel de Volume do Mixer de udio (dB)" + "Nível de Volume do Mixer de Áudio (dB)" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_MUTE, - "Mixer de udio Mudo" + "Mixer de Áudio Mudo" ) MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MIXER_MUTE, - "Mixer de udio mudo/no-mudo." + "Mixer de áudio mudo/não-mudo." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_ONLINE_UPDATER, "Exibir Atualizador Online" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_ONLINE_UPDATER, - "Exibir a opo 'Atualizador Online'." + "Exibir a opção 'Atualizador Online'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_VIEWS_SETTINGS, - "Visualizaes") + "Visualizações") MSG_HASH( MENU_ENUM_SUBLABEL_MENU_VIEWS_SETTINGS, "Exibir elementos na tela de menu." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_CORE_UPDATER, - "Exibir Atualizador de Ncleos" + "Exibir Atualizador de Núcleos" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_CORE_UPDATER, - "Exibir a opo de atualizar ncleos (e arquivos de informao de ncleo)." + "Exibir a opção de atualizar núcleos (e arquivos de informação de núcleo)." ) MSG_HASH(MSG_PREPARING_FOR_CONTENT_SCAN, - "Preparando a busca de contedo..." + "Preparando a busca de conteúdo..." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_DELETE, - "Remover ncleo" + "Remover núcleo" ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_DELETE, - "Remover este ncleo do disco.") + "Remover este núcleo do disco.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_FRAMEBUFFER_OPACITY, "Opacidade do Framebuffer" ) @@ -4065,13 +4065,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES, "Favoritos" ) MSG_HASH(MENU_ENUM_SUBLABEL_GOTO_FAVORITES, - "Contedo adicionado aos 'Favoritos' vai aparecer aqui." + "Conteúdo adicionado aos 'Favoritos' vai aparecer aqui." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GOTO_MUSIC, - "Msicas" + "Músicas" ) MSG_HASH(MENU_ENUM_SUBLABEL_GOTO_MUSIC, - "Msicas que foram reproduzidas aparecem aqui." + "Músicas que foram reproduzidas aparecem aqui." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GOTO_IMAGES, "Imagens" @@ -4080,22 +4080,22 @@ MSG_HASH(MENU_ENUM_SUBLABEL_GOTO_IMAGES, "Imagens que foram exibidas aparecem aqui." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_GOTO_VIDEO, - "Vdeos" + "Vídeos" ) MSG_HASH(MENU_ENUM_SUBLABEL_GOTO_VIDEO, - "Vdeos que foram reproduzidos aparecem aqui." + "Vídeos que foram reproduzidos aparecem aqui." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MATERIALUI_ICONS_ENABLE, - "cones do Menu" + "Ícones do Menu" ) MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_ICONS_ENABLE, - "Habilitar/desabilitar os cones exibidos do lado esquerdo dos itens de menu." + "Habilitar/desabilitar os ícones exibidos do lado esquerdo dos itens de menu." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_MAIN_MENU_ENABLE_SETTINGS, - "Habilitar guia de configuraes" + "Habilitar guia de configurações" ) MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS_PASSWORD, - "Definir senha para habilitar guia de configuraes" + "Definir senha para habilitar guia de configurações" ) MSG_HASH(MSG_INPUT_ENABLE_SETTINGS_PASSWORD, "Digite a senha" @@ -4107,160 +4107,160 @@ MSG_HASH(MSG_INPUT_ENABLE_SETTINGS_PASSWORD_NOK, "Senha incorreta." ) MSG_HASH(MENU_ENUM_SUBLABEL_XMB_MAIN_MENU_ENABLE_SETTINGS, - "Habilita a guia de configuraes. necessrio reiniciar para que a guia aparea." + "Habilita a guia de configurações. É necessário reiniciar para que a guia apareça." ) MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_SETTINGS_PASSWORD, - "O fornecimento de uma senha ao ocultar a guia de configuraes permite restaurar mais tarde a partir do menu, indo para a guia Menu Principal, selecionando Habilitar guia configuraes e inserindo a senha." + "O fornecimento de uma senha ao ocultar a guia de configurações permite restaurar mais tarde a partir do menu, indo para a guia Menu Principal, selecionando Habilitar guia configurações e inserindo a senha." ) MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_ENTRY_RENAME, - "Permita que o usurio renomeie as entradas nas colees." + "Permita que o usuário renomeie as entradas nas coleções." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_PLAYLIST_ENTRY_RENAME, "Permitir renomear entradas" ) MSG_HASH(MENU_ENUM_SUBLABEL_RENAME_ENTRY, - "Renomear o ttulo da entrada.") + "Renomear o título da entrada.") MSG_HASH(MENU_ENUM_LABEL_VALUE_RENAME_ENTRY, "Renomear") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_LOAD_CORE, - "Exibir Carregar Ncleo" + "Exibir Carregar Núcleo" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_LOAD_CORE, - "Exibir/ocultar a opo 'Carregar Ncleo'." + "Exibir/ocultar a opção 'Carregar Núcleo'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_LOAD_CONTENT, - "Exibir Carregar Contedo" + "Exibir Carregar Conteúdo" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_LOAD_CONTENT, - "Exibir/ocultar a opo 'Carregar Contedo'." + "Exibir/ocultar a opção 'Carregar Conteúdo'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_INFORMATION, - "Exibir Informao" + "Exibir Informação" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_INFORMATION, - "Exibir/ocultar a opo 'Informao'." + "Exibir/ocultar a opção 'Informação'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_CONFIGURATIONS, - "Exibir Configuraes" + "Exibir Configurações" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_CONFIGURATIONS, - "Exibir/ocultar a opo 'Configuraes'." + "Exibir/ocultar a opção 'Configurações'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_HELP, "Exibir Ajuda" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_HELP, - "Exibir/ocultar a opo 'Ajuda'." + "Exibir/ocultar a opção 'Ajuda'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_QUIT_RETROARCH, "Exibir Sair do RetroArch" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_QUIT_RETROARCH, - "Exibir/ocultar a opo 'Sair do RetroArch'." + "Exibir/ocultar a opção 'Sair do RetroArch'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_REBOOT, "Exibir Reiniciar" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_REBOOT, - "Exibir/ocultar a opo 'Reiniciar'." + "Exibir/ocultar a opção 'Reiniciar'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_VIEWS_SETTINGS, - "Menu Rpido" + "Menu Rápido" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_VIEWS_SETTINGS, - "Exibir ou ocultar elementos na tela de Menu Rpido." + "Exibir ou ocultar elementos na tela de Menu Rápido." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_TAKE_SCREENSHOT, "Exibir Captura de Tela" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_TAKE_SCREENSHOT, - "Exibir/ocultar a opo 'Captura de Tela'." + "Exibir/ocultar a opção 'Captura de Tela'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SAVE_LOAD_STATE, "Exibir Salvar/Carregar Estado" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_LOAD_STATE, - "Exibir/ocultar as opes para salvar/carregar estados." + "Exibir/ocultar as opções para salvar/carregar estados." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_UNDO_SAVE_LOAD_STATE, "Exibir Desfazer Salvar/Carregar Estado" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_UNDO_SAVE_LOAD_STATE, - "Exibir/ocultar as opes para abolir o salvar/carregar estado." + "Exibir/ocultar as opções para abolir o salvar/carregar estado." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_ADD_TO_FAVORITES, "Exibir Adicionar aos Favoritos" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_ADD_TO_FAVORITES, - "Exibir/ocultar a opo 'Adicionar aos Favoritos'." + "Exibir/ocultar a opção 'Adicionar aos Favoritos'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_OPTIONS, - "Exibir Opes" + "Exibir Opções" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_OPTIONS, - "Exibir/ocultar a opo 'Opes'." + "Exibir/ocultar a opção 'Opções'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_CONTROLS, "Exibir Controles" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_CONTROLS, - "Exibir/ocultar a opo 'Controles'." + "Exibir/ocultar a opção 'Controles'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_CHEATS, - "Exibir Trapaas" + "Exibir Trapaças" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_CHEATS, - "Exibir/ocultar a opo 'Trapaas'." + "Exibir/ocultar a opção 'Trapaças'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SHADERS, "Exibir Shaders" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SHADERS, - "Exibir/ocultar a opo 'Shaders'." + "Exibir/ocultar a opção 'Shaders'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SAVE_CORE_OVERRIDES, - "Exibir Salvar Redefinio de Ncleo" + "Exibir Salvar Redefinição de Núcleo" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_CORE_OVERRIDES, - "Exibir/ocultar a opo 'Salvar Redefinio de Ncleo'." + "Exibir/ocultar a opção 'Salvar Redefinição de Núcleo'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_SAVE_GAME_OVERRIDES, - "Exibir Salvar Redefinio de Jogo" + "Exibir Salvar Redefinição de Jogo" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_GAME_OVERRIDES, - "Exibir/ocultar a opo 'Salvar Redefinio de Jogo'." + "Exibir/ocultar a opção 'Salvar Redefinição de Jogo'." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_INFORMATION, - "Exibir Informao" + "Exibir Informação" ) MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_INFORMATION, - "Exibir/ocultar a opo 'Informao'.") + "Exibir/ocultar a opção 'Informação'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_ENABLE, - "Ativar Notificao de Fundo") + "Ativar Notificação de Fundo") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_RED, - "Notificao de Fundo em Cor Vermelha") + "Notificação de Fundo em Cor Vermelha") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_GREEN, - "Notificao de Fundo em Cor Verde") + "Notificação de Fundo em Cor Verde") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_BLUE, - "Notificao de Fundo em Cor Azul") + "Notificação de Fundo em Cor Azul") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_BGCOLOR_OPACITY, - "Opacidade da Notificao de Fundo") + "Opacidade da Notificação de Fundo") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_DISABLE_KIOSK_MODE, "Desabilitar o Modo Quiosque" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_DISABLE_KIOSK_MODE, - "Desabilita o Modo Quiosque. necessria uma reinicializao para que a mudana tenha total efeito." + "Desabilita o Modo Quiosque. É necessária uma reinicialização para que a mudança tenha total efeito." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_ENABLE_KIOSK_MODE, "Habilitar o Modo Quiosque" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENABLE_KIOSK_MODE, - "Protege a configurao escondendo todas as configuraes relacionadas configurao." + "Protege a configuração escondendo todas as configurações relacionadas à configuração." ) MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_KIOSK_MODE_PASSWORD, "Definir senha para desabilitar o Modo Quiosque" ) MSG_HASH(MENU_ENUM_SUBLABEL_MENU_KIOSK_MODE_PASSWORD, - "Fornecer uma senha ao habilitar o Modo Quiosque tornando possvel desabilitar mais tarde a partir do menu, indo para o Menu Principal, selecionando Desabilitar o Modo Quiosque e inserindo a senha." + "Fornecer uma senha ao habilitar o Modo Quiosque tornando possível desabilitar mais tarde a partir do menu, indo para o Menu Principal, selecionando Desabilitar o Modo Quiosque e inserindo a senha." ) MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD, "Digite a Senha" @@ -4271,38 +4271,38 @@ MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD_OK, MSG_HASH(MSG_INPUT_KIOSK_MODE_PASSWORD_NOK, "Senha incorreta.") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_RED, - "Notificao em Cor Vermelha") + "Notificação em Cor Vermelha") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_GREEN, - "Notificao em Cor Verde") + "Notificação em Cor Verde") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_MESSAGE_COLOR_BLUE, - "Notificao em Cor Azul") + "Notificação em Cor Azul") MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAMECOUNT_SHOW, "Mostrar contagem de quadros na tela FPS") MSG_HASH(MSG_CONFIG_OVERRIDE_LOADED, - "Substituio de configurao carregada.") + "Substituição de configuração carregada.") MSG_HASH(MSG_GAME_REMAP_FILE_LOADED, "Arquivo de remapeamento do jogo carregado.") MSG_HASH(MSG_CORE_REMAP_FILE_LOADED, "Arquivo de remapeamento principal carregado.") MSG_HASH(MENU_ENUM_LABEL_VALUE_AUTOMATICALLY_ADD_CONTENT_TO_PLAYLIST, - "Adicione automaticamente contedo lista de reproduo") + "Adicione automaticamente conteúdo à lista de reprodução") MSG_HASH(MENU_ENUM_SUBLABEL_AUTOMATICALLY_ADD_CONTENT_TO_PLAYLIST, - "Verifica automaticamente o contedo carregado para que eles apaream dentro das listas de reproduo.") + "Verifica automaticamente o conteúdo carregado para que eles apareçam dentro das listas de reprodução.") MSG_HASH(MSG_SCANNING_OF_FILE_FINISHED, - "Verificao do arquivo terminado") + "Verificação do arquivo terminado") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_OPACITY, "Opacidade da Janela") MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_QUALITY, - "Qualidade da Reamostragem do udio") + "Qualidade da Reamostragem do Áudio") MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_QUALITY, - "Abaixe esse valor para favorecer o desempenho/baixa latncia em relao qualidade de udio, aumente se desejar melhor qualidade de udio custa do desempenho/baixa latncia.") + "Abaixe esse valor para favorecer o desempenho/baixa latência em relação à qualidade de áudio, aumente se desejar melhor qualidade de áudio à custa do desempenho/baixa latência.") MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_WATCH_FOR_CHANGES, - "Ver arquivos de shader para mudanas") + "Ver arquivos de shader para mudanças") MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_WATCH_FOR_CHANGES, - "Aplicar automaticamente as alteraes feitas nos arquivos de shader no disco.") + "Aplicar automaticamente as alterações feitas nos arquivos de shader no disco.") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_SHOW_DECORATIONS, - "Mostrar Decoraes da Janela") + "Mostrar Decorações da Janela") MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, - "Exibir estatsticas") + "Exibir estatísticas") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, - "Mostrar estatsticas tcnicas na tela.") + "Mostrar estatísticas técnicas na tela.") From 97cf6613da6d2443c71c23757ca2b46cd1d55bd0 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Apr 2018 02:20:17 +0200 Subject: [PATCH 276/517] (XMB/Shaders) Reimplement menu shader loading for GLSL/Cg/HLSL backends - if XMB is selected, it will compile the shader pipelines in advance --- gfx/drivers/vulkan.c | 7 +- gfx/drivers_shader/shader_gl_cg.c | 74 ++++++----- gfx/drivers_shader/shader_glsl.c | 206 ++++++++++++++++-------------- gfx/drivers_shader/shader_hlsl.c | 1 + gfx/drivers_shader/shader_null.c | 1 + gfx/video_driver.c | 7 +- gfx/video_driver.h | 1 + 7 files changed, 164 insertions(+), 133 deletions(-) diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index cb62be8ffc..0c6ed1c4b0 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -201,8 +201,7 @@ static void vulkan_init_pipeline_layout( &layout_info, NULL, &vk->pipelines.layout); } -static void vulkan_init_pipelines( - vk_t *vk) +static void vulkan_init_pipelines(vk_t *vk) { static const uint32_t alpha_blend_vert[] = #include "vulkan_shaders/alpha_blend.vert.inc" @@ -834,7 +833,11 @@ static bool vulkan_init_filter_chain(vk_t *vk) static void vulkan_init_resources(vk_t *vk) { + if (!vk) + return; + vk->num_swapchain_images = vk->context->num_swapchain_images; + vulkan_init_framebuffers(vk); vulkan_init_pipelines(vk); vulkan_init_descriptor_pool(vk); diff --git a/gfx/drivers_shader/shader_gl_cg.c b/gfx/drivers_shader/shader_gl_cg.c index 2fed59a303..4e89540b63 100644 --- a/gfx/drivers_shader/shader_gl_cg.c +++ b/gfx/drivers_shader/shader_gl_cg.c @@ -1053,10 +1053,50 @@ static void gl_cg_set_program_attributes(void *data, unsigned i) } } +static void gl_cg_init_menu_shaders(void *data) +{ + struct shader_program_info shader_prog_info; + cg_shader_data_t *cg = (cg_shader_data_t*)data; + + if (!cg) + return; + +#ifdef HAVE_SHADERPIPELINE + shader_prog_info.combined = stock_xmb_ribbon_simple; + shader_prog_info.is_file = false; + + gl_cg_compile_program( + cg, + VIDEO_SHADER_MENU, + &cg->prg[VIDEO_SHADER_MENU], + &shader_prog_info); + gl_cg_set_program_base_attrib(cg, VIDEO_SHADER_MENU); + + shader_prog_info.combined = stock_xmb_ribbon_simple; + shader_prog_info.is_file = false; + + gl_cg_compile_program( + cg, + VIDEO_SHADER_MENU_2, + &cg->prg[VIDEO_SHADER_MENU_2], + &shader_prog_info); + gl_cg_set_program_base_attrib(cg, VIDEO_SHADER_MENU_2); + + shader_prog_info.combined = stock_xmb_snow; + shader_prog_info.is_file = false; + + gl_cg_compile_program( + cg, + VIDEO_SHADER_MENU_3, + &cg->prg[VIDEO_SHADER_MENU_3], + &shader_prog_info); + gl_cg_set_program_base_attrib(cg, VIDEO_SHADER_MENU_3); +#endif +} + static void *gl_cg_init(void *data, const char *path) { unsigned i; - struct shader_program_info shader_prog_info; cg_shader_data_t *cg = (cg_shader_data_t*) calloc(1, sizeof(cg_shader_data_t)); @@ -1129,37 +1169,6 @@ static void *gl_cg_init(void *data, const char *path) gl_cg_set_shaders(cg->prg[1].fprg, cg->prg[1].vprg); -#ifdef HAVE_SHADERPIPELINE - shader_prog_info.combined = stock_xmb_ribbon_simple; - shader_prog_info.is_file = false; - - gl_cg_compile_program( - cg, - VIDEO_SHADER_MENU, - &cg->prg[VIDEO_SHADER_MENU], - &shader_prog_info); - gl_cg_set_program_base_attrib(cg, VIDEO_SHADER_MENU); - - shader_prog_info.combined = stock_xmb_ribbon_simple; - shader_prog_info.is_file = false; - - gl_cg_compile_program( - cg, - VIDEO_SHADER_MENU_2, - &cg->prg[VIDEO_SHADER_MENU_2], - &shader_prog_info); - gl_cg_set_program_base_attrib(cg, VIDEO_SHADER_MENU_2); - - shader_prog_info.combined = stock_xmb_snow; - shader_prog_info.is_file = false; - - gl_cg_compile_program( - cg, - VIDEO_SHADER_MENU_3, - &cg->prg[VIDEO_SHADER_MENU_3], - &shader_prog_info); - gl_cg_set_program_base_attrib(cg, VIDEO_SHADER_MENU_3); -#endif gl_cg_reset_attrib(cg); @@ -1271,6 +1280,7 @@ static struct video_shader *gl_cg_get_current_shader(void *data) const shader_backend_t gl_cg_backend = { gl_cg_init, + gl_cg_init_menu_shaders, gl_cg_deinit, gl_cg_set_params, gl_cg_set_uniform_parameter, diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index 7485cb341c..de04fb332d 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -790,6 +790,113 @@ static void gl_glsl_deinit(void *data) free(glsl); } +static void gl_glsl_init_menu_shaders(void *data) +{ +#ifdef HAVE_SHADERPIPELINE + struct shader_program_info shader_prog_info; + glsl_shader_data_t *glsl = (glsl_shader_data_t*)data; + + if (!glsl) + return; + +#ifdef HAVE_OPENGLES + if (gl_query_extension("GL_OES_standard_derivatives")) + { + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy; + shader_prog_info.fragment = glsl_core ? core_stock_fragment_xmb : stock_fragment_xmb; + } + else + { + shader_prog_info.vertex = stock_vertex_xmb_ribbon_simple_legacy; + shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple; + } +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy; + shader_prog_info.fragment = glsl_core ? core_stock_fragment_xmb : stock_fragment_xmb; +#endif + shader_prog_info.is_file = false; + + gl_glsl_compile_program( + glsl, + VIDEO_SHADER_MENU, + &glsl->prg[VIDEO_SHADER_MENU], + &shader_prog_info); + gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU].id, + &glsl->uniforms[VIDEO_SHADER_MENU]); + + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_simple_modern : stock_vertex_xmb_ribbon_simple_legacy; + shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple; + + gl_glsl_compile_program( + glsl, + VIDEO_SHADER_MENU_2, + &glsl->prg[VIDEO_SHADER_MENU_2], + &shader_prog_info); + gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_2].id, + &glsl->uniforms[VIDEO_SHADER_MENU_2]); + +#if defined(HAVE_OPENGLES) + shader_prog_info.vertex = stock_vertex_xmb_snow_modern; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; +#endif + shader_prog_info.fragment = stock_fragment_xmb_simple_snow; + + gl_glsl_compile_program( + glsl, + VIDEO_SHADER_MENU_3, + &glsl->prg[VIDEO_SHADER_MENU_3], + &shader_prog_info); + gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_3].id, + &glsl->uniforms[VIDEO_SHADER_MENU_3]); + +#if defined(HAVE_OPENGLES) + shader_prog_info.vertex = stock_vertex_xmb_snow_modern; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; +#endif + shader_prog_info.fragment = stock_fragment_xmb_snow; + + gl_glsl_compile_program( + glsl, + VIDEO_SHADER_MENU_4, + &glsl->prg[VIDEO_SHADER_MENU_4], + &shader_prog_info); + gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_4].id, + &glsl->uniforms[VIDEO_SHADER_MENU_4]); + +#if defined(HAVE_OPENGLES) + shader_prog_info.vertex = stock_vertex_xmb_snow_modern; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; +#endif + shader_prog_info.fragment = stock_fragment_xmb_bokeh; + + gl_glsl_compile_program( + glsl, + VIDEO_SHADER_MENU_5, + &glsl->prg[VIDEO_SHADER_MENU_5], + &shader_prog_info); + gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_5].id, + &glsl->uniforms[VIDEO_SHADER_MENU_5]); + +#if defined(HAVE_OPENGLES) + shader_prog_info.vertex = stock_vertex_xmb_snow_modern; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; +#endif + shader_prog_info.fragment = stock_fragment_xmb_snowflake; + + gl_glsl_compile_program( + glsl, + VIDEO_SHADER_MENU_6, + &glsl->prg[VIDEO_SHADER_MENU_6], + &shader_prog_info); + gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_6].id, + &glsl->uniforms[VIDEO_SHADER_MENU_6]); +#endif +} + static void *gl_glsl_init(void *data, const char *path) { unsigned i; @@ -1015,104 +1122,6 @@ static void *gl_glsl_init(void *data, const char *path) glsl->uniforms[VIDEO_SHADER_STOCK_BLEND] = glsl->uniforms[0]; } -#ifdef HAVE_SHADERPIPELINE -#ifdef HAVE_OPENGLES - if (gl_query_extension("GL_OES_standard_derivatives")) - { - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy; - shader_prog_info.fragment = glsl_core ? core_stock_fragment_xmb : stock_fragment_xmb; - } - else - { - shader_prog_info.vertex = stock_vertex_xmb_ribbon_simple_legacy; - shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple; - } -#else - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy; - shader_prog_info.fragment = glsl_core ? core_stock_fragment_xmb : stock_fragment_xmb; -#endif - shader_prog_info.is_file = false; - - gl_glsl_compile_program( - glsl, - VIDEO_SHADER_MENU, - &glsl->prg[VIDEO_SHADER_MENU], - &shader_prog_info); - gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU].id, - &glsl->uniforms[VIDEO_SHADER_MENU]); - - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_simple_modern : stock_vertex_xmb_ribbon_simple_legacy; - shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple; - - gl_glsl_compile_program( - glsl, - VIDEO_SHADER_MENU_2, - &glsl->prg[VIDEO_SHADER_MENU_2], - &shader_prog_info); - gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_2].id, - &glsl->uniforms[VIDEO_SHADER_MENU_2]); - -#if defined(HAVE_OPENGLES) - shader_prog_info.vertex = stock_vertex_xmb_snow_modern; -#else - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; -#endif - shader_prog_info.fragment = stock_fragment_xmb_simple_snow; - - gl_glsl_compile_program( - glsl, - VIDEO_SHADER_MENU_3, - &glsl->prg[VIDEO_SHADER_MENU_3], - &shader_prog_info); - gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_3].id, - &glsl->uniforms[VIDEO_SHADER_MENU_3]); - -#if defined(HAVE_OPENGLES) - shader_prog_info.vertex = stock_vertex_xmb_snow_modern; -#else - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; -#endif - shader_prog_info.fragment = stock_fragment_xmb_snow; - - gl_glsl_compile_program( - glsl, - VIDEO_SHADER_MENU_4, - &glsl->prg[VIDEO_SHADER_MENU_4], - &shader_prog_info); - gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_4].id, - &glsl->uniforms[VIDEO_SHADER_MENU_4]); - -#if defined(HAVE_OPENGLES) - shader_prog_info.vertex = stock_vertex_xmb_snow_modern; -#else - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; -#endif - shader_prog_info.fragment = stock_fragment_xmb_bokeh; - - gl_glsl_compile_program( - glsl, - VIDEO_SHADER_MENU_5, - &glsl->prg[VIDEO_SHADER_MENU_5], - &shader_prog_info); - gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_5].id, - &glsl->uniforms[VIDEO_SHADER_MENU_5]); - -#if defined(HAVE_OPENGLES) - shader_prog_info.vertex = stock_vertex_xmb_snow_modern; -#else - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; -#endif - shader_prog_info.fragment = stock_fragment_xmb_snowflake; - - gl_glsl_compile_program( - glsl, - VIDEO_SHADER_MENU_6, - &glsl->prg[VIDEO_SHADER_MENU_6], - &shader_prog_info); - gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_6].id, - &glsl->uniforms[VIDEO_SHADER_MENU_6]); -#endif - gl_glsl_reset_attrib(glsl); for (i = 0; i < GFX_MAX_SHADERS; i++) @@ -1688,6 +1697,7 @@ void gl_glsl_set_context_type(bool core_profile, const shader_backend_t gl_glsl_backend = { gl_glsl_init, + gl_glsl_init_menu_shaders, gl_glsl_deinit, gl_glsl_set_params, gl_glsl_set_uniform_parameter, diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index be50cad69d..1e86acc584 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -592,6 +592,7 @@ static struct video_shader *hlsl_get_current_shader(void *data) const shader_backend_t hlsl_backend = { hlsl_init, + NULL, /* hlsl_init_menu_shaders */ hlsl_deinit, hlsl_set_params, hlsl_set_uniform_parameter, diff --git a/gfx/drivers_shader/shader_null.c b/gfx/drivers_shader/shader_null.c index d85cfd1b97..792c03f759 100644 --- a/gfx/drivers_shader/shader_null.c +++ b/gfx/drivers_shader/shader_null.c @@ -77,6 +77,7 @@ static bool shader_null_compile_program( const shader_backend_t shader_null_backend = { shader_null_init, + NULL, shader_null_deinit, NULL, shader_null_set_uniform_parameter, diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 3c796f1659..fe9a6cbf66 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -3545,7 +3545,8 @@ bool video_shader_driver_init_first(void) bool video_shader_driver_init(video_shader_ctx_init_t *init) { - void *tmp = NULL; + void *tmp = NULL; + settings_t *settings = config_get_ptr(); if (!init->shader || !init->shader->init) { @@ -3560,6 +3561,10 @@ bool video_shader_driver_init(video_shader_ctx_init_t *init) if (!tmp) return false; + if (string_is_equal(settings->arrays.menu_driver, "xmb") + && init->shader->init_menu_shaders) + init->shader->init_menu_shaders(tmp); + current_shader_data = tmp; current_shader = (shader_backend_t*)init->shader; video_shader_driver_reset_to_defaults(); diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 878aea7bae..e72552c047 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -200,6 +200,7 @@ struct uniform_info typedef struct shader_backend { void *(*init)(void *data, const char *path); + void (*init_menu_shaders)(void *data); void (*deinit)(void *data); /* Set shader parameters. */ From 801ecb019a3b0de6856b71e5fedcc4a5f6855b52 Mon Sep 17 00:00:00 2001 From: David Walters Date: Thu, 12 Apr 2018 12:44:42 +0100 Subject: [PATCH 277/517] patch retro_opendir to handle nullptr and empty-string input --- libretro-common/file/retro_dirent.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libretro-common/file/retro_dirent.c b/libretro-common/file/retro_dirent.c index 9c53504732..6c9de40b0b 100644 --- a/libretro-common/file/retro_dirent.c +++ b/libretro-common/file/retro_dirent.c @@ -108,9 +108,13 @@ struct RDIR *retro_opendir(const char *name) #endif struct RDIR *rdir = (struct RDIR*)calloc(1, sizeof(*rdir)); - if (!rdir) + if (!rdir||!name) return NULL; + /* Handle empty string as current dir */ + if (*name==0) + name="."; + #if defined(_WIN32) (void)path_wide; (void)path_local; From 3f77a6a320ac96e97b7fa7a891f7d7782ec48be8 Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Thu, 12 Apr 2018 18:56:54 +0200 Subject: [PATCH 278/517] MUI Scale mouse cursor like icons. --- menu/drivers/materialui.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 8d4e0deb7b..e28dc238e9 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -1778,6 +1778,7 @@ static void materialui_layout(materialui_handle_t *mui, bool video_is_threaded) static void *materialui_init(void **userdata, bool video_is_threaded) { + float scale_factor = menu_display_get_dpi(); materialui_handle_t *mui = NULL; menu_handle_t *menu = (menu_handle_t*) calloc(1, sizeof(*menu)); @@ -1794,7 +1795,7 @@ static void *materialui_init(void **userdata, bool video_is_threaded) goto error; *userdata = mui; - mui->cursor_size = 64.0; + mui->cursor_size = scale_factor / 3; mui->need_compute = false; return menu; From 5486894e3bea860a586a7d82cb77362a3f0a1c32 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Apr 2018 20:16:58 +0200 Subject: [PATCH 279/517] (PS3) Set GCC for PS3 --- Makefile.ps3 | 2 +- Makefile.ps3.cobra | 2 +- Makefile.ps3.salamander | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.ps3 b/Makefile.ps3 index f3f29d18de..3e47cfe536 100644 --- a/Makefile.ps3 +++ b/Makefile.ps3 @@ -2,7 +2,7 @@ include version.all #which compiler to build with - GCC or SNC #set to GCC for debug builds for use with debugger -CELL_BUILD_TOOLS = SNC +CELL_BUILD_TOOLS = GCC CELL_GPU_TYPE = RSX CELL_PSGL_VERSION = ultra-opt diff --git a/Makefile.ps3.cobra b/Makefile.ps3.cobra index ce6f755c0a..3a270b2dd6 100644 --- a/Makefile.ps3.cobra +++ b/Makefile.ps3.cobra @@ -2,7 +2,7 @@ include version.all #which compiler to build with - GCC or SNC #set to GCC for debug builds for use with debugger -CELL_BUILD_TOOLS = SNC +CELL_BUILD_TOOLS = GCC CELL_GPU_TYPE = RSX CELL_PSGL_VERSION = ultra-opt diff --git a/Makefile.ps3.salamander b/Makefile.ps3.salamander index 49c8c9a719..c769ff105c 100644 --- a/Makefile.ps3.salamander +++ b/Makefile.ps3.salamander @@ -1,4 +1,4 @@ -CELL_BUILD_TOOLS = SNC +CELL_BUILD_TOOLS = GCC CELL_SDK ?= /usr/local/cell HAVE_LOGGER = 0 CELL_MK_DIR ?= $(CELL_SDK)/samples/mk From 249abcd10861a8c10c66230192c5f499ca85d995 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Apr 2018 20:30:35 +0200 Subject: [PATCH 280/517] Revert "(PS3) Set GCC for PS3" This reverts commit 5486894e3bea860a586a7d82cb77362a3f0a1c32. --- Makefile.ps3 | 2 +- Makefile.ps3.cobra | 2 +- Makefile.ps3.salamander | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.ps3 b/Makefile.ps3 index 3e47cfe536..f3f29d18de 100644 --- a/Makefile.ps3 +++ b/Makefile.ps3 @@ -2,7 +2,7 @@ include version.all #which compiler to build with - GCC or SNC #set to GCC for debug builds for use with debugger -CELL_BUILD_TOOLS = GCC +CELL_BUILD_TOOLS = SNC CELL_GPU_TYPE = RSX CELL_PSGL_VERSION = ultra-opt diff --git a/Makefile.ps3.cobra b/Makefile.ps3.cobra index 3a270b2dd6..ce6f755c0a 100644 --- a/Makefile.ps3.cobra +++ b/Makefile.ps3.cobra @@ -2,7 +2,7 @@ include version.all #which compiler to build with - GCC or SNC #set to GCC for debug builds for use with debugger -CELL_BUILD_TOOLS = GCC +CELL_BUILD_TOOLS = SNC CELL_GPU_TYPE = RSX CELL_PSGL_VERSION = ultra-opt diff --git a/Makefile.ps3.salamander b/Makefile.ps3.salamander index c769ff105c..49c8c9a719 100644 --- a/Makefile.ps3.salamander +++ b/Makefile.ps3.salamander @@ -1,4 +1,4 @@ -CELL_BUILD_TOOLS = GCC +CELL_BUILD_TOOLS = SNC CELL_SDK ?= /usr/local/cell HAVE_LOGGER = 0 CELL_MK_DIR ?= $(CELL_SDK)/samples/mk From e7bed050bc518d453c6aa4a104dfc2a5c387449c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Apr 2018 20:54:06 +0200 Subject: [PATCH 281/517] Fix memory leak --- libretro-common/file/retro_dirent.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libretro-common/file/retro_dirent.c b/libretro-common/file/retro_dirent.c index 6c9de40b0b..648739a93c 100644 --- a/libretro-common/file/retro_dirent.c +++ b/libretro-common/file/retro_dirent.c @@ -109,11 +109,11 @@ struct RDIR *retro_opendir(const char *name) struct RDIR *rdir = (struct RDIR*)calloc(1, sizeof(*rdir)); if (!rdir||!name) + { + if (rdir) + free(rdir); return NULL; - - /* Handle empty string as current dir */ - if (*name==0) - name="."; + } #if defined(_WIN32) (void)path_wide; From 6e4cf412f0ccf0f60d798f458a3f7e45bc8da78b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Apr 2018 21:02:54 +0200 Subject: [PATCH 282/517] (menu_entry_playlist_start_entry) This code seems to be unnecessary now? --- menu/cbs/menu_cbs_ok.c | 58 ++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index c561763986..8cab13ff5b 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -1650,30 +1650,18 @@ static int action_ok_playlist_entry_start_content(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { size_t selection_ptr = 0; - bool playlist_initialized = false; - playlist_t *playlist = NULL; const char *entry_path = NULL; const char *entry_label = NULL; const char *core_path = NULL; const char *core_name = NULL; menu_handle_t *menu = NULL; - playlist_t *tmp_playlist = playlist_get_cached(); + playlist_t *playlist = playlist_get_cached(); - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + if ( !playlist || + !menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) return menu_cbs_exit(); - if (!tmp_playlist) - { - tmp_playlist = playlist_init( - menu->db_playlist_file, COLLECTION_SIZE); - - if (!tmp_playlist) - return menu_cbs_exit(); - playlist_initialized = true; - } - - playlist = tmp_playlist; - selection_ptr = menu->rdb_entry_start_game_selection_ptr; + selection_ptr = menu->rdb_entry_start_game_selection_ptr; playlist_get_index(playlist, selection_ptr, &entry_path, &entry_label, &core_path, &core_name, NULL, NULL); @@ -1700,32 +1688,26 @@ static int action_ok_playlist_entry_start_content(const char *path, if (!core_info_find(&core_info, new_core_path)) found_associated_core = false; + /* TODO: figure out if this should refer to + * the inner or outer entry_path. */ + /* TODO: make sure there's only one entry_path + * in this function. */ if (!found_associated_core) - { - /* TODO: figure out if this should refer to the inner or outer entry_path */ - /* TODO: make sure there's only one entry_path in this function */ - int ret = action_ok_file_load_with_detect_core(entry_path, + return action_ok_file_load_with_detect_core(entry_path, label, type, selection_ptr, entry_idx); - if (playlist_initialized) - playlist_free(tmp_playlist); - return ret; - } - tmp_playlist = playlist_get_cached(); - - if (tmp_playlist) - command_playlist_update_write( - tmp_playlist, - selection_ptr, - NULL, - NULL, - new_core_path, - core_info.inf->display_name, - NULL, - NULL); + command_playlist_update_write( + playlist, + selection_ptr, + NULL, + NULL, + new_core_path, + core_info.inf->display_name, + NULL, + NULL); } - if (!playlist || !menu_content_playlist_load(playlist, selection_ptr)) + if (!menu_content_playlist_load(playlist, selection_ptr)) { runloop_msg_queue_push("File could not be loaded from playlist.\n", 1, 100, true); goto error; @@ -1737,8 +1719,6 @@ static int action_ok_playlist_entry_start_content(const char *path, return default_action_ok_load_content_from_playlist_from_menu(core_path, path, entry_label); error: - if (playlist_initialized) - playlist_free(tmp_playlist); return menu_cbs_exit(); } From 9a355b8f2e4b33d941cc3e2895350e60b4de89be Mon Sep 17 00:00:00 2001 From: radius Date: Thu, 12 Apr 2018 14:20:38 -0500 Subject: [PATCH 283/517] remap-redux: try to fix more warnings --- input/input_remapping.c | 130 ++++++++++++++++++---------------- menu/cbs/menu_cbs_get_value.c | 2 +- menu/cbs/menu_cbs_left.c | 3 +- menu/cbs/menu_cbs_right.c | 3 +- 4 files changed, 71 insertions(+), 67 deletions(-) diff --git a/input/input_remapping.c b/input/input_remapping.c index 96ffd40557..964d78d8ab 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -39,7 +39,7 @@ static unsigned old_libretro_device[MAX_USERS]; **/ bool input_remapping_load_file(void *data, const char *path) { - unsigned i, j; + unsigned i, j, k; config_file_t *conf = (config_file_t*)data; settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); @@ -54,9 +54,9 @@ bool input_remapping_load_file(void *data, const char *path) for (i = 0; i < MAX_USERS; i++) { char s1[64], s2[64], s3[64]; - char btn_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char key_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char stk_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; + char btn_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}}; + char key_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}}; + char stk_ident[8][128] = {{0}}; char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = { "b", "y", "select", "start", @@ -75,52 +75,55 @@ bool input_remapping_load_file(void *data, const char *path) snprintf(s2, sizeof(s2), "input_player%u_key", i + 1); snprintf(s3, sizeof(s3), "input_player%u_stk", i + 1); - for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) + for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) { - int btn_remap = -1; - int key_remap = -1; + if (j < RARCH_FIRST_CUSTOM_BIND) + { + int btn_remap = -1; + int key_remap = -1; - fill_pathname_join_delim(btn_ident[j], s1, - key_strings[j], '_', sizeof(btn_ident[j])); - fill_pathname_join_delim(key_ident[j], s2, - key_strings[j], '_', sizeof(btn_ident[j])); + fill_pathname_join_delim(btn_ident[j], s1, + key_strings[j], '_', sizeof(btn_ident[j])); + fill_pathname_join_delim(key_ident[j], s2, + key_strings[j], '_', sizeof(btn_ident[j])); - if (config_get_int(conf, btn_ident[j], &btn_remap) - && btn_remap != -1) - settings->uints.input_remap_ids[i][j] = btn_remap; - else if (config_get_int(conf, btn_ident[j], &btn_remap) - && btn_remap == -1) - settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; - /* else do nothing, important */ + if (config_get_int(conf, btn_ident[j], &btn_remap) + && btn_remap != -1) + settings->uints.input_remap_ids[i][j] = btn_remap; + else if (config_get_int(conf, btn_ident[j], &btn_remap) + && btn_remap == -1) + settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; + /* else do nothing, important */ - if (config_get_int(conf, key_ident[j], &key_remap)) - settings->uints.input_keymapper_ids[i][j] = key_remap; + if (config_get_int(conf, key_ident[j], &key_remap)) + settings->uints.input_keymapper_ids[i][j] = key_remap; + else + settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; + } else - settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; - } + { + int stk_remap = -1; + k = j - RARCH_FIRST_CUSTOM_BIND; - for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) - { - int stk_remap = -1; + fill_pathname_join_delim(stk_ident[k], s3, + key_strings[j], '$', sizeof(stk_ident[k])); - fill_pathname_join_delim(stk_ident[j], s3, - key_strings[j], '$', sizeof(stk_ident[j])); + snprintf(stk_ident[k], + sizeof(stk_ident[k]), + "%s_%s", + s3, + key_strings[j]); - snprintf(stk_ident[j], - sizeof(stk_ident[j]), - "%s_%s", - s3, - key_strings[j]); + /* RARCH_LOG("pre_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]); */ - /* RARCH_LOG("pre_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]); */ + if (config_get_int(conf, stk_ident[k], &stk_remap) && stk_remap != -1) + settings->uints.input_remap_ids[i][j] = stk_remap; + else if (config_get_int(conf, stk_ident[k], &stk_remap) && stk_remap == -1) + settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; + /* else do nothing, important */ - if (config_get_int(conf, stk_ident[j], &stk_remap) && stk_remap != -1) - settings->uints.input_remap_ids[i][j] = stk_remap; - else if (config_get_int(conf, stk_ident[j], &stk_remap) && stk_remap == -1) - settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; - /* else do nothing, important */ - - /*RARCH_LOG("stk_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]);*/ + /*RARCH_LOG("stk_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]);*/ + } } snprintf(s1, sizeof(s1), "input_player%u_analog_dpad_mode", i + 1); @@ -146,7 +149,7 @@ bool input_remapping_load_file(void *data, const char *path) bool input_remapping_save_file(const char *path) { bool ret; - unsigned i, j; + unsigned i, j, k; size_t path_size = PATH_MAX_LENGTH * sizeof(char); char *buf = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); char *remap_file = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); @@ -178,9 +181,9 @@ bool input_remapping_save_file(const char *path) for (i = 0; i < max_users; i++) { char s1[64], s2[64], s3[64]; - char btn_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char key_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char stk_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; + char btn_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}}; + char key_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}}; + char stk_ident[8][128] = {{0}}; char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = { "b", "y", "select", "start", @@ -197,14 +200,15 @@ bool input_remapping_save_file(const char *path) for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) { - fill_pathname_join_delim(btn_ident[j], s1, - key_strings[j], '_', sizeof(btn_ident[j])); - fill_pathname_join_delim(key_ident[j], s2, - key_strings[j], '_', sizeof(btn_ident[j])); - /* only save values that have been modified */ if(j < RARCH_FIRST_CUSTOM_BIND) { + fill_pathname_join_delim(btn_ident[j], s1, + key_strings[j], '_', sizeof(btn_ident[j])); + fill_pathname_join_delim(key_ident[j], s2, + key_strings[j], '_', sizeof(btn_ident[j])); + + /* only save values that have been modified */ if(settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] != RARCH_UNMAPPED) config_set_int(conf, btn_ident[j], settings->uints.input_remap_ids[i][j]); @@ -221,15 +225,16 @@ bool input_remapping_save_file(const char *path) } else { - fill_pathname_join_delim(stk_ident[j], s3, - key_strings[j], '_', sizeof(stk_ident[j])); + k = j - RARCH_FIRST_CUSTOM_BIND; + fill_pathname_join_delim(stk_ident[k], s3, + key_strings[j], '_', sizeof(stk_ident[k])); if(settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] != RARCH_UNMAPPED) - config_set_int(conf, stk_ident[j], + config_set_int(conf, stk_ident[k], settings->uints.input_remap_ids[i][j]); else if(settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] == RARCH_UNMAPPED) - config_set_int(conf, stk_ident[j], + config_set_int(conf, stk_ident[k], -1); else config_unset(conf,btn_ident[j]); @@ -289,16 +294,17 @@ void input_remapping_set_defaults(bool deinit) for (i = 0; i < MAX_USERS; i++) { - for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) + for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) { - const struct retro_keybind *keybind = &input_config_binds[i][j]; - if (keybind) - settings->uints.input_remap_ids[i][j] = keybind->id; - settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; - } - for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) - { - settings->uints.input_remap_ids[i][j] = j; + if (j < RARCH_FIRST_CUSTOM_BIND) + { + const struct retro_keybind *keybind = &input_config_binds[i][j]; + if (keybind) + settings->uints.input_remap_ids[i][j] = keybind->id; + settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; + } + else + settings->uints.input_remap_ids[i][j] = j; } if (old_analog_dpad_mode[i]) diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 6557e3f982..6ccfb0f31a 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -618,7 +618,7 @@ static void menu_action_setting_disp_set_label_input_desc_kbd( remap_id = settings->uints.input_keymapper_ids[offset][id]; - for (key_id = 0; key_id < RARCH_MAX_KEYS; key_id++) + for (key_id = 0; key_id < RARCH_MAX_KEYS - 1; key_id++) { if(remap_id == key_descriptors[key_id].key) break; diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 04a403ca2e..6e306b3b97 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -394,8 +394,7 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, remap_id = settings->uints.input_keymapper_ids[offset][id]; - for (key_id = 0; key_id < RARCH_MAX_KEYS + - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) + for (key_id = 0; key_id < RARCH_MAX_KEYS - 1; key_id++) { if(remap_id == key_descriptors[key_id].key) break; diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 0f2fa245a6..fd5c9ff8f1 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -120,8 +120,7 @@ int action_right_input_desc_kbd(unsigned type, const char *label, remap_id = settings->uints.input_keymapper_ids[offset][id]; - for (key_id = 0; key_id < MENU_SETTINGS_INPUT_DESC_KBD_END - - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; key_id++) + for (key_id = 0; key_id < RARCH_MAX_KEYS - 1; key_id++) { if(remap_id == key_descriptors[key_id].key) break; From 6761ec471ddbe95c9a86aaefe71ff66e22f7daa1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Apr 2018 21:39:31 +0200 Subject: [PATCH 284/517] Silence some Coverity warnings --- cheevos/cheevos.c | 4 +- input/input_remapping.c | 130 +++++++++++++++++---------------- network/netplay/netplay_sync.c | 3 +- tasks/task_overlay.c | 5 +- 4 files changed, 72 insertions(+), 70 deletions(-) diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 556ac9942d..35d4857b55 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -1334,10 +1334,8 @@ static int cheevos_new_lboard(cheevos_readud_t *ud) if (!ldb || !ud) return -1; - lboard = ldb + ud->lboard_count++; - if (!lboard) - return -1; + lboard = ldb + ud->lboard_count++; lboard->id = (unsigned)strtol(ud->id.string, NULL, 10); lboard->format = cheevos_parse_format(&ud->format); diff --git a/input/input_remapping.c b/input/input_remapping.c index 96ffd40557..964d78d8ab 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -39,7 +39,7 @@ static unsigned old_libretro_device[MAX_USERS]; **/ bool input_remapping_load_file(void *data, const char *path) { - unsigned i, j; + unsigned i, j, k; config_file_t *conf = (config_file_t*)data; settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); @@ -54,9 +54,9 @@ bool input_remapping_load_file(void *data, const char *path) for (i = 0; i < MAX_USERS; i++) { char s1[64], s2[64], s3[64]; - char btn_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char key_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char stk_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; + char btn_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}}; + char key_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}}; + char stk_ident[8][128] = {{0}}; char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = { "b", "y", "select", "start", @@ -75,52 +75,55 @@ bool input_remapping_load_file(void *data, const char *path) snprintf(s2, sizeof(s2), "input_player%u_key", i + 1); snprintf(s3, sizeof(s3), "input_player%u_stk", i + 1); - for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) + for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) { - int btn_remap = -1; - int key_remap = -1; + if (j < RARCH_FIRST_CUSTOM_BIND) + { + int btn_remap = -1; + int key_remap = -1; - fill_pathname_join_delim(btn_ident[j], s1, - key_strings[j], '_', sizeof(btn_ident[j])); - fill_pathname_join_delim(key_ident[j], s2, - key_strings[j], '_', sizeof(btn_ident[j])); + fill_pathname_join_delim(btn_ident[j], s1, + key_strings[j], '_', sizeof(btn_ident[j])); + fill_pathname_join_delim(key_ident[j], s2, + key_strings[j], '_', sizeof(btn_ident[j])); - if (config_get_int(conf, btn_ident[j], &btn_remap) - && btn_remap != -1) - settings->uints.input_remap_ids[i][j] = btn_remap; - else if (config_get_int(conf, btn_ident[j], &btn_remap) - && btn_remap == -1) - settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; - /* else do nothing, important */ + if (config_get_int(conf, btn_ident[j], &btn_remap) + && btn_remap != -1) + settings->uints.input_remap_ids[i][j] = btn_remap; + else if (config_get_int(conf, btn_ident[j], &btn_remap) + && btn_remap == -1) + settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; + /* else do nothing, important */ - if (config_get_int(conf, key_ident[j], &key_remap)) - settings->uints.input_keymapper_ids[i][j] = key_remap; + if (config_get_int(conf, key_ident[j], &key_remap)) + settings->uints.input_keymapper_ids[i][j] = key_remap; + else + settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; + } else - settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; - } + { + int stk_remap = -1; + k = j - RARCH_FIRST_CUSTOM_BIND; - for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) - { - int stk_remap = -1; + fill_pathname_join_delim(stk_ident[k], s3, + key_strings[j], '$', sizeof(stk_ident[k])); - fill_pathname_join_delim(stk_ident[j], s3, - key_strings[j], '$', sizeof(stk_ident[j])); + snprintf(stk_ident[k], + sizeof(stk_ident[k]), + "%s_%s", + s3, + key_strings[j]); - snprintf(stk_ident[j], - sizeof(stk_ident[j]), - "%s_%s", - s3, - key_strings[j]); + /* RARCH_LOG("pre_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]); */ - /* RARCH_LOG("pre_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]); */ + if (config_get_int(conf, stk_ident[k], &stk_remap) && stk_remap != -1) + settings->uints.input_remap_ids[i][j] = stk_remap; + else if (config_get_int(conf, stk_ident[k], &stk_remap) && stk_remap == -1) + settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; + /* else do nothing, important */ - if (config_get_int(conf, stk_ident[j], &stk_remap) && stk_remap != -1) - settings->uints.input_remap_ids[i][j] = stk_remap; - else if (config_get_int(conf, stk_ident[j], &stk_remap) && stk_remap == -1) - settings->uints.input_remap_ids[i][j] = RARCH_UNMAPPED; - /* else do nothing, important */ - - /*RARCH_LOG("stk_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]);*/ + /*RARCH_LOG("stk_ident: %s:%d\n", stk_ident[j], settings->uints.input_remap_ids[i][j]);*/ + } } snprintf(s1, sizeof(s1), "input_player%u_analog_dpad_mode", i + 1); @@ -146,7 +149,7 @@ bool input_remapping_load_file(void *data, const char *path) bool input_remapping_save_file(const char *path) { bool ret; - unsigned i, j; + unsigned i, j, k; size_t path_size = PATH_MAX_LENGTH * sizeof(char); char *buf = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); char *remap_file = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); @@ -178,9 +181,9 @@ bool input_remapping_save_file(const char *path) for (i = 0; i < max_users; i++) { char s1[64], s2[64], s3[64]; - char btn_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char key_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; - char stk_ident[RARCH_FIRST_CUSTOM_BIND + 4][128] = {{0}}; + char btn_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}}; + char key_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}}; + char stk_ident[8][128] = {{0}}; char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = { "b", "y", "select", "start", @@ -197,14 +200,15 @@ bool input_remapping_save_file(const char *path) for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) { - fill_pathname_join_delim(btn_ident[j], s1, - key_strings[j], '_', sizeof(btn_ident[j])); - fill_pathname_join_delim(key_ident[j], s2, - key_strings[j], '_', sizeof(btn_ident[j])); - /* only save values that have been modified */ if(j < RARCH_FIRST_CUSTOM_BIND) { + fill_pathname_join_delim(btn_ident[j], s1, + key_strings[j], '_', sizeof(btn_ident[j])); + fill_pathname_join_delim(key_ident[j], s2, + key_strings[j], '_', sizeof(btn_ident[j])); + + /* only save values that have been modified */ if(settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] != RARCH_UNMAPPED) config_set_int(conf, btn_ident[j], settings->uints.input_remap_ids[i][j]); @@ -221,15 +225,16 @@ bool input_remapping_save_file(const char *path) } else { - fill_pathname_join_delim(stk_ident[j], s3, - key_strings[j], '_', sizeof(stk_ident[j])); + k = j - RARCH_FIRST_CUSTOM_BIND; + fill_pathname_join_delim(stk_ident[k], s3, + key_strings[j], '_', sizeof(stk_ident[k])); if(settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] != RARCH_UNMAPPED) - config_set_int(conf, stk_ident[j], + config_set_int(conf, stk_ident[k], settings->uints.input_remap_ids[i][j]); else if(settings->uints.input_remap_ids[i][j] != j && settings->uints.input_remap_ids[i][j] == RARCH_UNMAPPED) - config_set_int(conf, stk_ident[j], + config_set_int(conf, stk_ident[k], -1); else config_unset(conf,btn_ident[j]); @@ -289,16 +294,17 @@ void input_remapping_set_defaults(bool deinit) for (i = 0; i < MAX_USERS; i++) { - for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++) + for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) { - const struct retro_keybind *keybind = &input_config_binds[i][j]; - if (keybind) - settings->uints.input_remap_ids[i][j] = keybind->id; - settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; - } - for (j = RARCH_FIRST_CUSTOM_BIND; j < RARCH_FIRST_CUSTOM_BIND + 8; j++) - { - settings->uints.input_remap_ids[i][j] = j; + if (j < RARCH_FIRST_CUSTOM_BIND) + { + const struct retro_keybind *keybind = &input_config_binds[i][j]; + if (keybind) + settings->uints.input_remap_ids[i][j] = keybind->id; + settings->uints.input_keymapper_ids[i][j] = RETROK_UNKNOWN; + } + else + settings->uints.input_remap_ids[i][j] = j; } if (old_analog_dpad_mode[i]) diff --git a/network/netplay/netplay_sync.c b/network/netplay/netplay_sync.c index 7da8a893c1..0035ac7baf 100644 --- a/network/netplay/netplay_sync.c +++ b/network/netplay/netplay_sync.c @@ -292,7 +292,8 @@ static void merge_analog_part(netplay_t *netplay, } if (share_mode == NETPLAY_SHARE_ANALOG_AVERAGE) - value /= client_count; + if (client_count > 0) /* Prevent potential divide by zero */ + value /= client_count; resstate->data[word] |= ((uint32_t) (uint16_t) value) << bit; } diff --git a/tasks/task_overlay.c b/tasks/task_overlay.c index 72c6046c8d..7f755f6ef9 100644 --- a/tasks/task_overlay.c +++ b/tasks/task_overlay.c @@ -327,13 +327,10 @@ static bool task_overlay_resolve_targets(struct overlay *ol, unsigned i; struct overlay *current = (struct overlay*)&ol[idx]; - if (!current) - return false; - for (i = 0; i < current->size; i++) { struct overlay_desc *desc = (struct overlay_desc*)¤t->descs[i]; - const char *next = desc ? desc->next_index_name : NULL; + const char *next = desc->next_index_name; ssize_t next_idx = (idx + 1) & size; if (!string_is_empty(next)) From 70abbbf4fbd9d3195065130435df41fa331b673c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Apr 2018 23:46:41 +0200 Subject: [PATCH 285/517] Revert "Revert "(PS3) Set GCC for PS3"" This reverts commit 249abcd10861a8c10c66230192c5f499ca85d995. --- Makefile.ps3 | 2 +- Makefile.ps3.cobra | 2 +- Makefile.ps3.salamander | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.ps3 b/Makefile.ps3 index f3f29d18de..3e47cfe536 100644 --- a/Makefile.ps3 +++ b/Makefile.ps3 @@ -2,7 +2,7 @@ include version.all #which compiler to build with - GCC or SNC #set to GCC for debug builds for use with debugger -CELL_BUILD_TOOLS = SNC +CELL_BUILD_TOOLS = GCC CELL_GPU_TYPE = RSX CELL_PSGL_VERSION = ultra-opt diff --git a/Makefile.ps3.cobra b/Makefile.ps3.cobra index ce6f755c0a..3a270b2dd6 100644 --- a/Makefile.ps3.cobra +++ b/Makefile.ps3.cobra @@ -2,7 +2,7 @@ include version.all #which compiler to build with - GCC or SNC #set to GCC for debug builds for use with debugger -CELL_BUILD_TOOLS = SNC +CELL_BUILD_TOOLS = GCC CELL_GPU_TYPE = RSX CELL_PSGL_VERSION = ultra-opt diff --git a/Makefile.ps3.salamander b/Makefile.ps3.salamander index 49c8c9a719..c769ff105c 100644 --- a/Makefile.ps3.salamander +++ b/Makefile.ps3.salamander @@ -1,4 +1,4 @@ -CELL_BUILD_TOOLS = SNC +CELL_BUILD_TOOLS = GCC CELL_SDK ?= /usr/local/cell HAVE_LOGGER = 0 CELL_MK_DIR ?= $(CELL_SDK)/samples/mk From 1751f4a0afbc1c2ab21977413aac983a83279221 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 13 Apr 2018 00:18:11 +0200 Subject: [PATCH 286/517] Make it possible to read bigger files by replacing ssize_t with int64_t and size_t with uint64_t --- gfx/drivers_shader/glslang_util.cpp | 2 +- gfx/drivers_shader/shader_glsl.c | 2 +- libretro-common/audio/audio_mix.c | 2 +- libretro-common/file/archive_file.c | 4 +- libretro-common/include/file/archive_file.h | 2 +- libretro-common/include/streams/chd_stream.h | 4 +- libretro-common/include/streams/file_stream.h | 14 +++--- .../include/streams/interface_stream.h | 16 +++---- .../include/streams/memory_stream.h | 12 ++--- libretro-common/streams/chd_stream.c | 6 +-- libretro-common/streams/file_stream.c | 26 +++++------ libretro-common/streams/interface_stream.c | 24 +++++----- libretro-common/streams/memory_stream.c | 32 +++++++------- runahead/secondary_core.c | 2 +- tasks/task_content.c | 8 ++-- tasks/task_database.c | 40 ++++++++--------- tasks/task_database_cue.c | 44 +++++++++---------- tasks/task_patch.c | 6 +-- tasks/task_save.c | 2 +- 19 files changed, 124 insertions(+), 124 deletions(-) diff --git a/gfx/drivers_shader/glslang_util.cpp b/gfx/drivers_shader/glslang_util.cpp index 41d3527b36..05a648ca9b 100644 --- a/gfx/drivers_shader/glslang_util.cpp +++ b/gfx/drivers_shader/glslang_util.cpp @@ -44,7 +44,7 @@ bool glslang_read_shader_file(const char *path, vector *output, bool roo char tmp[PATH_MAX_LENGTH]; char *ptr = NULL; char *buf = nullptr; - ssize_t len = 0; + int64_t len = 0; const char *basename = path_basename(path); include_path[0] = tmp[0] = '\0'; diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index de04fb332d..cdabb79ec4 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -493,7 +493,7 @@ static void gl_glsl_strip_parameter_pragmas(char *source) static bool gl_glsl_load_source_path(struct video_shader_pass *pass, const char *path) { - ssize_t len; + int64_t len; int nitems = pass ? filestream_read_file(path, (void**)&pass->source.string.vertex, &len) : 0; diff --git a/libretro-common/audio/audio_mix.c b/libretro-common/audio/audio_mix.c index 776a9f21e7..bdf345a211 100644 --- a/libretro-common/audio/audio_mix.c +++ b/libretro-common/audio/audio_mix.c @@ -111,7 +111,7 @@ void audio_mix_free_chunk(audio_chunk_t *chunk) audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate) { int sample_size; - ssize_t len = 0; + int64_t len = 0; void *buf = NULL; audio_chunk_t *chunk = (audio_chunk_t*)calloc(1, sizeof(*chunk)); diff --git a/libretro-common/file/archive_file.c b/libretro-common/file/archive_file.c index ca39050746..4a3c387092 100644 --- a/libretro-common/file/archive_file.c +++ b/libretro-common/file/archive_file.c @@ -129,7 +129,7 @@ static void file_archive_free(file_archive_file_data_t *data) static file_archive_file_data_t* file_archive_open(const char *path) { - ssize_t ret = -1; + int64_t ret = -1; bool read_from_file = false; file_archive_file_data_t *data = (file_archive_file_data_t*) calloc(1, sizeof(*data)); @@ -722,7 +722,7 @@ error: */ int file_archive_compressed_read( const char * path, void **buf, - const char* optional_filename, ssize_t *length) + const char* optional_filename, int64_t *length) { const struct file_archive_file_backend *backend = NULL; int ret = 0; diff --git a/libretro-common/include/file/archive_file.h b/libretro-common/include/file/archive_file.h index b2b5971931..7f5ab9fb92 100644 --- a/libretro-common/include/file/archive_file.h +++ b/libretro-common/include/file/archive_file.h @@ -187,7 +187,7 @@ bool file_archive_perform_mode(const char *name, const char *valid_exts, int file_archive_compressed_read( const char* path, void **buf, - const char* optional_filename, ssize_t *length); + const char* optional_filename, int64_t *length); const struct file_archive_file_backend* file_archive_get_zlib_file_backend(void); const struct file_archive_file_backend* file_archive_get_7z_file_backend(void); diff --git a/libretro-common/include/streams/chd_stream.h b/libretro-common/include/streams/chd_stream.h index 86f0ccbcd0..53c364fb26 100644 --- a/libretro-common/include/streams/chd_stream.h +++ b/libretro-common/include/streams/chd_stream.h @@ -49,11 +49,11 @@ int chdstream_getc(chdstream_t *stream); char *chdstream_gets(chdstream_t *stream, char *buffer, size_t len); -size_t chdstream_tell(chdstream_t *stream); +uint64_t chdstream_tell(chdstream_t *stream); void chdstream_rewind(chdstream_t *stream); -int chdstream_seek(chdstream_t *stream, ssize_t offset, int whence); +int64_t chdstream_seek(chdstream_t *stream, int64_t offset, int whence); ssize_t chdstream_get_size(chdstream_t *stream); diff --git a/libretro-common/include/streams/file_stream.h b/libretro-common/include/streams/file_stream.h index 79fd92ed2e..1ae545dc8c 100644 --- a/libretro-common/include/streams/file_stream.h +++ b/libretro-common/include/streams/file_stream.h @@ -47,7 +47,7 @@ typedef struct RFILE RFILE; void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info); -ssize_t filestream_get_size(RFILE *stream); +int64_t filestream_get_size(RFILE *stream); /** * filestream_open: @@ -60,19 +60,19 @@ ssize_t filestream_get_size(RFILE *stream); **/ RFILE *filestream_open(const char *path, unsigned mode, unsigned hints); -ssize_t filestream_seek(RFILE *stream, ssize_t offset, int seek_position); +int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position); -ssize_t filestream_read(RFILE *stream, void *data, int64_t len); +int64_t filestream_read(RFILE *stream, void *data, int64_t len); -ssize_t filestream_write(RFILE *stream, const void *data, int64_t len); +int64_t filestream_write(RFILE *stream, const void *data, int64_t len); -ssize_t filestream_tell(RFILE *stream); +int64_t filestream_tell(RFILE *stream); void filestream_rewind(RFILE *stream); int filestream_close(RFILE *stream); -int filestream_read_file(const char *path, void **buf, ssize_t *len); +int64_t filestream_read_file(const char *path, void **buf, int64_t *len); char *filestream_gets(RFILE *stream, char *s, size_t len); @@ -80,7 +80,7 @@ int filestream_getc(RFILE *stream); int filestream_eof(RFILE *stream); -bool filestream_write_file(const char *path, const void *data, ssize_t size); +bool filestream_write_file(const char *path, const void *data, int64_t size); int filestream_putc(RFILE *stream, int c); diff --git a/libretro-common/include/streams/interface_stream.h b/libretro-common/include/streams/interface_stream.h index 468d35eff3..bd1c89e2aa 100644 --- a/libretro-common/include/streams/interface_stream.h +++ b/libretro-common/include/streams/interface_stream.h @@ -68,29 +68,29 @@ bool intfstream_resize(intfstream_internal_t *intf, bool intfstream_open(intfstream_internal_t *intf, const char *path, unsigned mode, unsigned hints); -ssize_t intfstream_read(intfstream_internal_t *intf, - void *s, size_t len); +int64_t intfstream_read(intfstream_internal_t *intf, + void *s, uint64_t len); -ssize_t intfstream_write(intfstream_internal_t *intf, - const void *s, size_t len); +int64_t intfstream_write(intfstream_internal_t *intf, + const void *s, uint64_t len); char *intfstream_gets(intfstream_internal_t *intf, char *buffer, size_t len); int intfstream_getc(intfstream_internal_t *intf); -int intfstream_seek(intfstream_internal_t *intf, - int offset, int whence); +int64_t intfstream_seek(intfstream_internal_t *intf, + int64_t offset, int whence); void intfstream_rewind(intfstream_internal_t *intf); -int intfstream_tell(intfstream_internal_t *intf); +int64_t intfstream_tell(intfstream_internal_t *intf); void intfstream_putc(intfstream_internal_t *intf, int c); int intfstream_close(intfstream_internal_t *intf); -ssize_t intfstream_get_size(intfstream_internal_t *intf); +int64_t intfstream_get_size(intfstream_internal_t *intf); int intfstream_flush(intfstream_internal_t *intf); diff --git a/libretro-common/include/streams/memory_stream.h b/libretro-common/include/streams/memory_stream.h index dca8e7a6ed..fba8783ec5 100644 --- a/libretro-common/include/streams/memory_stream.h +++ b/libretro-common/include/streams/memory_stream.h @@ -36,9 +36,9 @@ memstream_t *memstream_open(unsigned writing); void memstream_close(memstream_t *stream); -size_t memstream_read(memstream_t *stream, void *data, size_t bytes); +uint64_t memstream_read(memstream_t *stream, void *data, uint64_t bytes); -size_t memstream_write(memstream_t *stream, const void *data, size_t bytes); +uint64_t memstream_write(memstream_t *stream, const void *data, uint64_t bytes); int memstream_getc(memstream_t *stream); @@ -46,15 +46,15 @@ void memstream_putc(memstream_t *stream, int c); char *memstream_gets(memstream_t *stream, char *buffer, size_t len); -size_t memstream_pos(memstream_t *stream); +uint64_t memstream_pos(memstream_t *stream); void memstream_rewind(memstream_t *stream); -int memstream_seek(memstream_t *stream, int offset, int whence); +int64_t memstream_seek(memstream_t *stream, int64_t offset, int whence); -void memstream_set_buffer(uint8_t *buffer, size_t size); +void memstream_set_buffer(uint8_t *buffer, uint64_t size); -size_t memstream_get_last_size(void); +uint64_t memstream_get_last_size(void); RETRO_END_DECLS diff --git a/libretro-common/streams/chd_stream.c b/libretro-common/streams/chd_stream.c index ebd736f0ad..406e9197e6 100644 --- a/libretro-common/streams/chd_stream.c +++ b/libretro-common/streams/chd_stream.c @@ -386,7 +386,7 @@ char *chdstream_gets(chdstream_t *stream, char *buffer, size_t len) return buffer; } -size_t chdstream_tell(chdstream_t *stream) +uint64_t chdstream_tell(chdstream_t *stream) { return stream->offset; } @@ -396,9 +396,9 @@ void chdstream_rewind(chdstream_t *stream) stream->offset = 0; } -int chdstream_seek(chdstream_t *stream, ssize_t offset, int whence) +int64_t chdstream_seek(chdstream_t *stream, int64_t offset, int whence) { - ssize_t new_offset; + int64_t new_offset; switch (whence) { diff --git a/libretro-common/streams/file_stream.c b/libretro-common/streams/file_stream.c index aa3bda1c78..e5df920b80 100644 --- a/libretro-common/streams/file_stream.c +++ b/libretro-common/streams/file_stream.c @@ -111,9 +111,9 @@ bool filestream_exists(const char *path) return true; } -ssize_t filestream_get_size(RFILE *stream) +int64_t filestream_get_size(RFILE *stream) { - ssize_t output; + int64_t output; if (filestream_size_cb != NULL) output = filestream_size_cb(stream->hfile); @@ -191,7 +191,7 @@ int filestream_getc(RFILE *stream) return EOF; } -ssize_t filestream_seek(RFILE *stream, ssize_t offset, int seek_position) +int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position) { int64_t output; @@ -213,9 +213,9 @@ int filestream_eof(RFILE *stream) } -ssize_t filestream_tell(RFILE *stream) +int64_t filestream_tell(RFILE *stream) { - ssize_t output; + int64_t output; if (filestream_size_cb != NULL) output = filestream_tell_cb(stream->hfile); @@ -237,7 +237,7 @@ void filestream_rewind(RFILE *stream) stream->eof_flag = false; } -ssize_t filestream_read(RFILE *stream, void *s, int64_t len) +int64_t filestream_read(RFILE *stream, void *s, int64_t len) { int64_t output; @@ -294,7 +294,7 @@ const char *filestream_get_path(RFILE *stream) return retro_vfs_file_get_path_impl((libretro_vfs_implementation_file*)stream->hfile); } -ssize_t filestream_write(RFILE *stream, const void *s, int64_t len) +int64_t filestream_write(RFILE *stream, const void *s, int64_t len) { int64_t output; @@ -373,9 +373,9 @@ int filestream_close(RFILE *stream) * * Returns: number of items read, -1 on error. */ -int filestream_read_file(const char *path, void **buf, ssize_t *len) +int64_t filestream_read_file(const char *path, void **buf, int64_t *len) { - ssize_t ret = 0; + int64_t ret = 0; int64_t content_buf_size = 0; void *content_buf = NULL; RFILE *file = filestream_open(path, @@ -393,11 +393,11 @@ int filestream_read_file(const char *path, void **buf, ssize_t *len) if (content_buf_size < 0) goto error; - content_buf = malloc((size_t)(content_buf_size + 1)); + content_buf = malloc((uint64_t)(content_buf_size + 1)); if (!content_buf) goto error; - if ((int64_t)(size_t)(content_buf_size + 1) != (content_buf_size + 1)) + if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1)) goto error; ret = filestream_read(file, content_buf, (int64_t)content_buf_size); @@ -441,9 +441,9 @@ error: * * Returns: true (1) on success, false (0) otherwise. */ -bool filestream_write_file(const char *path, const void *data, ssize_t size) +bool filestream_write_file(const char *path, const void *data, int64_t size) { - ssize_t ret = 0; + int64_t ret = 0; RFILE *file = filestream_open(path, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE); diff --git a/libretro-common/streams/interface_stream.c b/libretro-common/streams/interface_stream.c index a219a1525d..cba0f66004 100644 --- a/libretro-common/streams/interface_stream.c +++ b/libretro-common/streams/interface_stream.c @@ -57,7 +57,7 @@ struct intfstream_internal #endif }; -ssize_t intfstream_get_size(intfstream_internal_t *intf) +int64_t intfstream_get_size(intfstream_internal_t *intf) { if (!intf) return 0; @@ -219,7 +219,7 @@ error: return NULL; } -int intfstream_seek(intfstream_internal_t *intf, int offset, int whence) +int64_t intfstream_seek(intfstream_internal_t *intf, int64_t offset, int whence) { if (!intf) return -1; @@ -241,14 +241,14 @@ int intfstream_seek(intfstream_internal_t *intf, int offset, int whence) seek_position = RETRO_VFS_SEEK_POSITION_END; break; } - return (int)filestream_seek(intf->file.fp, (int)offset, + return (int64_t)filestream_seek(intf->file.fp, (int64_t)offset, seek_position); } case INTFSTREAM_MEMORY: - return (int)memstream_seek(intf->memory.fp, offset, whence); + return (int64_t)memstream_seek(intf->memory.fp, offset, whence); case INTFSTREAM_CHD: #ifdef HAVE_CHD - return (int)chdstream_seek(intf->chd.fp, offset, whence); + return (int64_t)chdstream_seek(intf->chd.fp, offset, whence); #else break; #endif @@ -257,7 +257,7 @@ int intfstream_seek(intfstream_internal_t *intf, int offset, int whence) return -1; } -ssize_t intfstream_read(intfstream_internal_t *intf, void *s, size_t len) +int64_t intfstream_read(intfstream_internal_t *intf, void *s, uint64_t len) { if (!intf) return 0; @@ -279,8 +279,8 @@ ssize_t intfstream_read(intfstream_internal_t *intf, void *s, size_t len) return -1; } -ssize_t intfstream_write(intfstream_internal_t *intf, - const void *s, size_t len) +int64_t intfstream_write(intfstream_internal_t *intf, + const void *s, uint64_t len) { if (!intf) return 0; @@ -343,7 +343,7 @@ int intfstream_getc(intfstream_internal_t *intf) return -1; } -int intfstream_tell(intfstream_internal_t *intf) +int64_t intfstream_tell(intfstream_internal_t *intf) { if (!intf) return -1; @@ -351,12 +351,12 @@ int intfstream_tell(intfstream_internal_t *intf) switch (intf->type) { case INTFSTREAM_FILE: - return (int)filestream_tell(intf->file.fp); + return (int64_t)filestream_tell(intf->file.fp); case INTFSTREAM_MEMORY: - return (int)memstream_pos(intf->memory.fp); + return (int64_t)memstream_pos(intf->memory.fp); case INTFSTREAM_CHD: #ifdef HAVE_CHD - return (int)chdstream_tell(intf->chd.fp); + return (int64_t)chdstream_tell(intf->chd.fp); #else break; #endif diff --git a/libretro-common/streams/memory_stream.c b/libretro-common/streams/memory_stream.c index 014101227d..57117f4de9 100644 --- a/libretro-common/streams/memory_stream.c +++ b/libretro-common/streams/memory_stream.c @@ -26,16 +26,16 @@ #include -static uint8_t* g_buffer = NULL; -static size_t g_size = 0; -static size_t last_file_size = 0; +static uint8_t* g_buffer = NULL; +static uint64_t g_size = 0; +static uint64_t last_file_size = 0; struct memstream { uint8_t *buf; - size_t size; - size_t ptr; - size_t max_ptr; + uint64_t size; + uint64_t ptr; + uint64_t max_ptr; unsigned writing; }; @@ -45,19 +45,19 @@ static void memstream_update_pos(memstream_t *stream) stream->max_ptr = stream->ptr; } -void memstream_set_buffer(uint8_t *buffer, size_t size) +void memstream_set_buffer(uint8_t *buffer, uint64_t size) { g_buffer = buffer; g_size = size; } -size_t memstream_get_last_size(void) +uint64_t memstream_get_last_size(void) { return last_file_size; } static void memstream_init(memstream_t *stream, - uint8_t *buffer, size_t max_size, unsigned writing) + uint8_t *buffer, uint64_t max_size, unsigned writing) { if (!stream) return; @@ -92,9 +92,9 @@ void memstream_close(memstream_t *stream) free(stream); } -size_t memstream_read(memstream_t *stream, void *data, size_t bytes) +uint64_t memstream_read(memstream_t *stream, void *data, uint64_t bytes) { - size_t avail = 0; + uint64_t avail = 0; if (!stream) return 0; @@ -109,9 +109,9 @@ size_t memstream_read(memstream_t *stream, void *data, size_t bytes) return bytes; } -size_t memstream_write(memstream_t *stream, const void *data, size_t bytes) +uint64_t memstream_write(memstream_t *stream, const void *data, uint64_t bytes) { - size_t avail = 0; + uint64_t avail = 0; if (!stream) return 0; @@ -126,9 +126,9 @@ size_t memstream_write(memstream_t *stream, const void *data, size_t bytes) return bytes; } -int memstream_seek(memstream_t *stream, int offset, int whence) +int64_t memstream_seek(memstream_t *stream, int64_t offset, int whence) { - size_t ptr; + uint64_t ptr; switch (whence) { @@ -159,7 +159,7 @@ void memstream_rewind(memstream_t *stream) memstream_seek(stream, 0L, SEEK_SET); } -size_t memstream_pos(memstream_t *stream) +uint64_t memstream_pos(memstream_t *stream) { return stream->ptr; } diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index d64e123136..b90c220649 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -91,7 +91,7 @@ char* copy_core_to_temp_file(void) char *retroarchTempPath = NULL; char *tempDllPath = NULL; void *dllFileData = NULL; - ssize_t dllFileSize = 0; + int64_t dllFileSize = 0; const char *corePath = path_get(RARCH_PATH_CORE); const char *coreBaseName = path_basename(corePath); diff --git a/tasks/task_content.c b/tasks/task_content.c index 273caa570f..0e11171773 100644 --- a/tasks/task_content.c +++ b/tasks/task_content.c @@ -151,7 +151,7 @@ static char pending_subsystem_extensions[PATH_MAX_LENGTH]; static char *pending_subsystem_roms[RARCH_MAX_SUBSYSTEM_ROMS]; -static int content_file_read(const char *path, void **buf, ssize_t *length) +static int64_t content_file_read(const char *path, void **buf, int64_t *length) { #ifdef HAVE_COMPRESSION if (path_contains_compressed_file(path)) @@ -324,7 +324,7 @@ end: static bool load_content_into_memory( content_information_ctx_t *content_ctx, unsigned i, const char *path, void **buf, - ssize_t *length) + int64_t *length) { uint8_t *ret_buf = NULL; @@ -383,7 +383,7 @@ static bool load_content_from_compressed_archive( char **error_string) { union string_list_elem_attr attributes; - ssize_t new_path_len = 0; + int64_t new_path_len = 0; size_t path_size = PATH_MAX_LENGTH * sizeof(char); char *new_basedir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); char *new_path = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); @@ -582,7 +582,7 @@ static bool content_file_load( { /* Load the content into memory. */ - ssize_t len = 0; + int64_t len = 0; if (!load_content_into_memory( content_ctx, diff --git a/tasks/task_database.c b/tasks/task_database.c index 96c0055d8d..4f616e15c4 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -70,17 +70,17 @@ typedef struct db_handle } db_handle_t; int cue_find_track(const char *cue_path, bool first, - size_t *offset, size_t *size, - char *track_path, size_t max_len); + uint64_t *offset, uint64_t *size, + char *track_path, uint64_t max_len); bool cue_next_file(intfstream_t *fd, const char *cue_path, - char *path, size_t max_len); + char *path, uint64_t max_len); int gdi_find_track(const char *gdi_path, bool first, - char *track_path, size_t max_len); + char *track_path, uint64_t max_len); bool gdi_next_file(intfstream_t *fd, const char *gdi_path, - char *path, size_t max_len); + char *path, uint64_t max_len); int detect_system(intfstream_t *fd, const char** system_name); @@ -204,11 +204,11 @@ static int intfstream_get_serial(intfstream_t *fd, char *serial) } static bool intfstream_file_get_serial(const char *name, - size_t offset, size_t size, char *serial) + uint64_t offset, uint64_t size, char *serial) { int rv; uint8_t *data = NULL; - ssize_t file_size = -1; + int64_t file_size = -1; intfstream_t *fd = intfstream_open_file(name, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); @@ -226,14 +226,14 @@ static bool intfstream_file_get_serial(const char *name, if (file_size < 0) goto error; - if (offset != 0 || size < (size_t) file_size) + if (offset != 0 || size < (uint64_t) file_size) { - if (intfstream_seek(fd, (int)offset, SEEK_SET) == -1) + if (intfstream_seek(fd, (int64_t)offset, SEEK_SET) == -1) goto error; data = (uint8_t*)malloc(size); - if (intfstream_read(fd, data, size) != (ssize_t) size) + if (intfstream_read(fd, data, size) != (int64_t) size) { free(data); goto error; @@ -268,8 +268,8 @@ static int task_database_cue_get_serial(const char *name, char* serial) char *track_path = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); int ret = 0; - size_t offset = 0; - size_t size = 0; + uint64_t offset = 0; + uint64_t size = 0; int rv = 0; track_path[0] = '\0'; @@ -340,7 +340,7 @@ static int task_database_chd_get_serial(const char *name, char* serial) static int intfstream_get_crc(intfstream_t *fd, uint32_t *crc) { - ssize_t read = 0; + int64_t read = 0; uint32_t acc = 0; uint8_t buffer[4096]; @@ -356,13 +356,13 @@ static int intfstream_get_crc(intfstream_t *fd, uint32_t *crc) } static bool intfstream_file_get_crc(const char *name, - size_t offset, size_t size, uint32_t *crc) + uint64_t offset, size_t size, uint32_t *crc) { int rv; intfstream_t *fd = intfstream_open_file(name, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); uint8_t *data = NULL; - ssize_t file_size = -1; + int64_t file_size = -1; if (!fd) return 0; @@ -378,14 +378,14 @@ static bool intfstream_file_get_crc(const char *name, if (file_size < 0) goto error; - if (offset != 0 || size < (size_t) file_size) + if (offset != 0 || size < (uint64_t) file_size) { - if (intfstream_seek(fd, (int)offset, SEEK_SET) == -1) + if (intfstream_seek(fd, (int64_t)offset, SEEK_SET) == -1) goto error; data = (uint8_t*)malloc(size); - if (intfstream_read(fd, data, size) != (ssize_t) size) + if (intfstream_read(fd, data, size) != (int64_t) size) goto error; intfstream_close(fd); @@ -417,8 +417,8 @@ error: static int task_database_cue_get_crc(const char *name, uint32_t *crc) { char *track_path = (char *)malloc(PATH_MAX_LENGTH); - size_t offset = 0; - size_t size = 0; + uint64_t offset = 0; + uint64_t size = 0; int rv = 0; track_path[0] = '\0'; diff --git a/tasks/task_database_cue.c b/tasks/task_database_cue.c index e6360d4d30..836e5b8bd3 100644 --- a/tasks/task_database_cue.c +++ b/tasks/task_database_cue.c @@ -64,15 +64,15 @@ static struct magic_entry MAGIC_NUMBERS[] = { { 0, NULL, NULL} }; -static ssize_t get_token(intfstream_t *fd, char *token, size_t max_len) +static int64_t get_token(intfstream_t *fd, char *token, uint64_t max_len) { char *c = token; - ssize_t len = 0; + int64_t len = 0; int in_string = 0; while (1) { - int rv = (int)intfstream_read(fd, c, 1); + int64_t rv = (int64_t)intfstream_read(fd, c, 1); if (rv == 0) return 0; @@ -116,7 +116,7 @@ static ssize_t get_token(intfstream_t *fd, char *token, size_t max_len) len++; c++; - if (len == (ssize_t)max_len) + if (len == (int64_t)max_len) { *c = '\0'; return len; @@ -362,7 +362,7 @@ int detect_system(intfstream_t *fd, const char **system_name) int rv; char magic[MAGIC_LEN]; int i; - ssize_t read; + int64_t read; RARCH_LOG("%s\n", msg_hash_to_str(MSG_COMPARING_WITH_KNOWN_MAGIC_NUMBERS)); for (i = 0; MAGIC_NUMBERS[i].system_name != NULL; i++) @@ -409,9 +409,9 @@ clean: return rv; } -static ssize_t intfstream_get_file_size(const char *path) +static int64_t intfstream_get_file_size(const char *path) { - ssize_t rv; + int64_t rv; intfstream_t *fd = intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); if (!fd) @@ -422,13 +422,13 @@ static ssize_t intfstream_get_file_size(const char *path) return rv; } -static bool update_cand(ssize_t *cand_index, ssize_t *last_index, - size_t *largest, char *last_file, size_t *offset, - size_t *size, char *track_path, size_t max_len) +static bool update_cand(int64_t *cand_index, int64_t *last_index, + uint64_t *largest, char *last_file, uint64_t *offset, + uint64_t *size, char *track_path, uint64_t max_len) { if (*cand_index != -1) { - if ((unsigned)(*last_index - *cand_index) > *largest) + if ((uint64_t)(*last_index - *cand_index) > *largest) { *largest = *last_index - *cand_index; strlcpy(track_path, last_file, max_len); @@ -443,19 +443,19 @@ static bool update_cand(ssize_t *cand_index, ssize_t *last_index, } int cue_find_track(const char *cue_path, bool first, - size_t *offset, size_t *size, char *track_path, size_t max_len) + uint64_t *offset, uint64_t *size, char *track_path, uint64_t max_len) { int rv; intfstream_info_t info; char *tmp_token = (char*)malloc(MAX_TOKEN_LEN); char *last_file = (char*)malloc(PATH_MAX_LENGTH + 1); intfstream_t *fd = NULL; - ssize_t last_index = -1; - ssize_t cand_index = -1; + int64_t last_index = -1; + int64_t cand_index = -1; int32_t cand_track = -1; int32_t track = 0; - size_t largest = 0; - ssize_t volatile file_size = -1; + uint64_t largest = 0; + int64_t volatile file_size = -1; bool is_data = false; char *cue_dir = (char*)malloc(PATH_MAX_LENGTH); cue_dir[0] = '\0'; @@ -604,16 +604,16 @@ bool cue_next_file(intfstream_t *fd, const char *cue_path, char *path, size_t ma } int gdi_find_track(const char *gdi_path, bool first, - char *track_path, size_t max_len) + char *track_path, uint64_t max_len) { int rv; intfstream_info_t info; char *tmp_token = (char*)malloc(MAX_TOKEN_LEN); intfstream_t *fd = NULL; - size_t largest = 0; + uint64_t largest = 0; int size = -1; int mode = -1; - ssize_t file_size = -1; + int64_t file_size = -1; info.type = INTFSTREAM_FILE; @@ -692,7 +692,7 @@ int gdi_find_track(const char *gdi_path, bool first, goto error; } - if ((unsigned)file_size > largest) + if ((uint64_t)file_size > largest) { strlcpy(track_path, last_file, max_len); rv = 0; @@ -733,11 +733,11 @@ error: } bool gdi_next_file(intfstream_t *fd, const char *gdi_path, - char *path, size_t max_len) + char *path, uint64_t max_len) { bool rv = false; char *tmp_token = (char*)malloc(MAX_TOKEN_LEN); - ssize_t offset = -1; + int64_t offset = -1; tmp_token[0] = '\0'; diff --git a/tasks/task_patch.c b/tasks/task_patch.c index 3df074c171..32079bc50c 100644 --- a/tasks/task_patch.c +++ b/tasks/task_patch.c @@ -551,7 +551,7 @@ static bool try_bps_patch(bool allow_bps, const char *name_bps, if (allow_bps && !string_is_empty(name_bps)) if (path_is_valid(name_bps) && filestream_exists(name_bps)) { - ssize_t patch_size; + int64_t patch_size; bool ret = false; void *patch_data = NULL; @@ -578,7 +578,7 @@ static bool try_ups_patch(bool allow_ups, const char *name_ups, if (allow_ups && !string_is_empty(name_ups)) if (path_is_valid(name_ups) && filestream_exists(name_ups)) { - ssize_t patch_size; + int64_t patch_size; bool ret = false; void *patch_data = NULL; @@ -605,7 +605,7 @@ static bool try_ips_patch(bool allow_ips, if (allow_ips && !string_is_empty(name_ips)) if (path_is_valid(name_ips) && filestream_exists(name_ips)) { - ssize_t patch_size; + int64_t patch_size; bool ret = false; void *patch_data = NULL; diff --git a/tasks/task_save.c b/tasks/task_save.c index 2dbca2c94d..bb944ea004 100644 --- a/tasks/task_save.c +++ b/tasks/task_save.c @@ -1347,7 +1347,7 @@ static bool content_get_memory(retro_ctx_memory_info_t *mem_info, */ bool content_load_ram_file(unsigned slot) { - ssize_t rc; + int64_t rc; struct ram_type ram; retro_ctx_memory_info_t mem_info; void *buf = NULL; From 041670fe027fc859296af9be91af3520cbb8d7e4 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 13 Apr 2018 00:47:42 +0200 Subject: [PATCH 287/517] Get rid of multitude of casting warnings --- Makefile.ps3 | 8 ++--- deps/stb/stb_rect_pack.h | 5 ---- .../include/streams/interface_stream.h | 4 +-- libretro-common/streams/file_stream.c | 2 +- libretro-common/streams/interface_stream.c | 14 +++++---- libretro-common/streams/memory_stream.c | 4 +-- tasks/task_content.c | 2 +- tasks/task_database.c | 8 ++--- tasks/task_database_cue.c | 15 ++++++---- tasks/task_patch.c | 30 +++++++++---------- tasks/task_save.c | 2 +- 11 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Makefile.ps3 b/Makefile.ps3 index 3e47cfe536..88f9dfe787 100644 --- a/Makefile.ps3 +++ b/Makefile.ps3 @@ -2,9 +2,9 @@ include version.all #which compiler to build with - GCC or SNC #set to GCC for debug builds for use with debugger -CELL_BUILD_TOOLS = GCC +CELL_BUILD_TOOLS = SNC CELL_GPU_TYPE = RSX -CELL_PSGL_VERSION = ultra-opt +CELL_PSGL_VERSION = opt ASSETS_DIR := media/assets @@ -96,7 +96,7 @@ ifeq ($(CELL_BUILD_TOOLS), SNC) PPU_CXXLD = $(CELL_SDK)/host-win32/sn/bin/ps3ppuld.exe PPU_CXX = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe PPU_CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe -else ifneq($(system_platform), win) +else ifneq ($(system_platform), win) PPU_CXX = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-g++.exe PPU_CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe PPU_CXXLD = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ld.exe @@ -119,7 +119,7 @@ DEFINES += -DHAVE_THREADS -DRARCH_CONSOLE -DHAVE_OPENGL -DHAVE_HEADSET -DHAVE_LA ifeq ($(DEBUG), 1) PPU_OPTIMIZE_LV := -O0 -g else - PPU_OPTIMIZE_LV := -O3 -g + PPU_OPTIMIZE_LV := -O2 -g endif ifeq ($(HAVE_LOGGER), 1) diff --git a/deps/stb/stb_rect_pack.h b/deps/stb/stb_rect_pack.h index 6c658de33e..ed1585ba19 100644 --- a/deps/stb/stb_rect_pack.h +++ b/deps/stb/stb_rect_pack.h @@ -528,12 +528,7 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n /* we use the 'was_packed' field internally to allow sorting/unsorting */ for (i=0; i < num_rects; ++i) - { rects[i].was_packed = i; - #ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); - #endif - } /* sort according to heuristic */ STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); diff --git a/libretro-common/include/streams/interface_stream.h b/libretro-common/include/streams/interface_stream.h index bd1c89e2aa..3332aa725b 100644 --- a/libretro-common/include/streams/interface_stream.h +++ b/libretro-common/include/streams/interface_stream.h @@ -75,7 +75,7 @@ int64_t intfstream_write(intfstream_internal_t *intf, const void *s, uint64_t len); char *intfstream_gets(intfstream_internal_t *intf, - char *buffer, size_t len); + char *buffer, uint64_t len); int intfstream_getc(intfstream_internal_t *intf); @@ -98,7 +98,7 @@ intfstream_t* intfstream_open_file(const char *path, unsigned mode, unsigned hints); intfstream_t *intfstream_open_memory(void *data, - unsigned mode, unsigned hints, size_t size); + unsigned mode, unsigned hints, uint64_t size); intfstream_t *intfstream_open_chd_track(const char *path, unsigned mode, unsigned hints, int32_t track); diff --git a/libretro-common/streams/file_stream.c b/libretro-common/streams/file_stream.c index e5df920b80..a73b78fbff 100644 --- a/libretro-common/streams/file_stream.c +++ b/libretro-common/streams/file_stream.c @@ -393,7 +393,7 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len) if (content_buf_size < 0) goto error; - content_buf = malloc((uint64_t)(content_buf_size + 1)); + content_buf = malloc((size_t)(content_buf_size + 1)); if (!content_buf) goto error; diff --git a/libretro-common/streams/interface_stream.c b/libretro-common/streams/interface_stream.c index cba0f66004..e495d9a9bf 100644 --- a/libretro-common/streams/interface_stream.c +++ b/libretro-common/streams/interface_stream.c @@ -43,7 +43,7 @@ struct intfstream_internal struct { uint8_t *data; - unsigned size; + uint64_t size; } buf; memstream_t *fp; bool writable; @@ -299,7 +299,7 @@ int64_t intfstream_write(intfstream_internal_t *intf, } char *intfstream_gets(intfstream_internal_t *intf, - char *buffer, size_t len) + char *buffer, uint64_t len) { if (!intf) return NULL; @@ -307,9 +307,11 @@ char *intfstream_gets(intfstream_internal_t *intf, switch (intf->type) { case INTFSTREAM_FILE: - return filestream_gets(intf->file.fp, buffer, len); + return filestream_gets(intf->file.fp, + buffer, (size_t)len); case INTFSTREAM_MEMORY: - return memstream_gets(intf->memory.fp, buffer, len); + return memstream_gets(intf->memory.fp, + buffer, (size_t)len); case INTFSTREAM_CHD: #ifdef HAVE_CHD return chdstream_gets(intf->chd.fp, buffer, len); @@ -428,14 +430,14 @@ error: } intfstream_t *intfstream_open_memory(void *data, - unsigned mode, unsigned hints, size_t size) + unsigned mode, unsigned hints, uint64_t size) { intfstream_info_t info; intfstream_t *fd = NULL; info.type = INTFSTREAM_MEMORY; info.memory.buf.data = (uint8_t*)data; - info.memory.buf.size = (unsigned)size; + info.memory.buf.size = size; info.memory.writable = false; fd = (intfstream_t*)intfstream_init(&info); diff --git a/libretro-common/streams/memory_stream.c b/libretro-common/streams/memory_stream.c index 57117f4de9..784d0feea1 100644 --- a/libretro-common/streams/memory_stream.c +++ b/libretro-common/streams/memory_stream.c @@ -103,7 +103,7 @@ uint64_t memstream_read(memstream_t *stream, void *data, uint64_t bytes) if (bytes > avail) bytes = avail; - memcpy(data, stream->buf + stream->ptr, bytes); + memcpy(data, stream->buf + stream->ptr, (size_t)bytes); stream->ptr += bytes; memstream_update_pos(stream); return bytes; @@ -120,7 +120,7 @@ uint64_t memstream_write(memstream_t *stream, const void *data, uint64_t bytes) if (bytes > avail) bytes = avail; - memcpy(stream->buf + stream->ptr, data, bytes); + memcpy(stream->buf + stream->ptr, data, (size_t)bytes); stream->ptr += bytes; memstream_update_pos(stream); return bytes; diff --git a/tasks/task_content.c b/tasks/task_content.c index 0e11171773..6d0b9a9c5f 100644 --- a/tasks/task_content.c +++ b/tasks/task_content.c @@ -359,7 +359,7 @@ static bool load_content_into_memory( (uint8_t**)&ret_buf, (void*)length); - content_rom_crc = encoding_crc32(0, ret_buf, *length); + content_rom_crc = encoding_crc32(0, ret_buf, (size_t)*length); RARCH_LOG("CRC32: 0x%x .\n", (unsigned)content_rom_crc); } diff --git a/tasks/task_database.c b/tasks/task_database.c index 4f616e15c4..2bbe1c4591 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -231,7 +231,7 @@ static bool intfstream_file_get_serial(const char *name, if (intfstream_seek(fd, (int64_t)offset, SEEK_SET) == -1) goto error; - data = (uint8_t*)malloc(size); + data = (uint8_t*)malloc((size_t)size); if (intfstream_read(fd, data, size) != (int64_t) size) { @@ -345,7 +345,7 @@ static int intfstream_get_crc(intfstream_t *fd, uint32_t *crc) uint8_t buffer[4096]; while ((read = intfstream_read(fd, buffer, sizeof(buffer))) > 0) - acc = encoding_crc32(acc, buffer, read); + acc = encoding_crc32(acc, buffer, (size_t)read); if (read < 0) return 0; @@ -435,11 +435,11 @@ static int task_database_cue_get_crc(const char *name, uint32_t *crc) return 0; } - RARCH_LOG("CUE '%s' primary track: %s\n (%lu, %lu)\n", name, track_path, (unsigned long) offset, (unsigned long) size); + RARCH_LOG("CUE '%s' primary track: %s\n (%lu, %lu)\n",name, track_path, (unsigned long) offset, (unsigned long) size); RARCH_LOG("%s\n", msg_hash_to_str(MSG_READING_FIRST_DATA_TRACK)); - rv = intfstream_file_get_crc(track_path, offset, size, crc); + rv = intfstream_file_get_crc(track_path, offset, (size_t)size, crc); if (rv == 1) { RARCH_LOG("CUE '%s' crc: %x\n", name, *crc); diff --git a/tasks/task_database_cue.c b/tasks/task_database_cue.c index 836e5b8bd3..b36cb4421c 100644 --- a/tasks/task_database_cue.c +++ b/tasks/task_database_cue.c @@ -431,7 +431,7 @@ static bool update_cand(int64_t *cand_index, int64_t *last_index, if ((uint64_t)(*last_index - *cand_index) > *largest) { *largest = *last_index - *cand_index; - strlcpy(track_path, last_file, max_len); + strlcpy(track_path, last_file, (size_t)max_len); *offset = *cand_index; *size = *largest; *cand_index = -1; @@ -576,7 +576,8 @@ error: return -errno; } -bool cue_next_file(intfstream_t *fd, const char *cue_path, char *path, size_t max_len) +bool cue_next_file(intfstream_t *fd, + const char *cue_path, char *path, uint64_t max_len) { bool rv = false; char *tmp_token = (char*)malloc(MAX_TOKEN_LEN); @@ -592,7 +593,7 @@ bool cue_next_file(intfstream_t *fd, const char *cue_path, char *path, size_t ma if (string_is_equal(tmp_token, "FILE")) { get_token(fd, tmp_token, MAX_TOKEN_LEN); - fill_pathname_join(path, cue_dir, tmp_token, max_len); + fill_pathname_join(path, cue_dir, tmp_token, (size_t)max_len); rv = true; break; } @@ -694,9 +695,11 @@ int gdi_find_track(const char *gdi_path, bool first, if ((uint64_t)file_size > largest) { - strlcpy(track_path, last_file, max_len); - rv = 0; + strlcpy(track_path, last_file, (size_t)max_len); + + rv = 0; largest = file_size; + if (first) { free(gdi_dir); @@ -767,7 +770,7 @@ bool gdi_next_file(intfstream_t *fd, const char *gdi_path, fill_pathname_basedir(gdi_dir, gdi_path, PATH_MAX_LENGTH); - fill_pathname_join(path, gdi_dir, tmp_token, max_len); + fill_pathname_join(path, gdi_dir, tmp_token, (size_t)max_len); rv = true; /* Disc offset */ diff --git a/tasks/task_patch.c b/tasks/task_patch.c index 32079bc50c..51579453ec 100644 --- a/tasks/task_patch.c +++ b/tasks/task_patch.c @@ -93,8 +93,8 @@ struct ups_data unsigned target_checksum; }; -typedef enum patch_error (*patch_func_t)(const uint8_t*, size_t, - const uint8_t*, size_t, uint8_t*, size_t*); +typedef enum patch_error (*patch_func_t)(const uint8_t*, uint64_t, + const uint8_t*, uint64_t, uint8_t*, uint64_t*); static uint8_t bps_read(struct bps_data *bps) { @@ -128,9 +128,9 @@ static void bps_write(struct bps_data *bps, uint8_t data) } static enum patch_error bps_apply_patch( - const uint8_t *modify_data, size_t modify_length, - const uint8_t *source_data, size_t source_length, - uint8_t *target_data, size_t *target_length) + const uint8_t *modify_data, uint64_t modify_length, + const uint8_t *source_data, uint64_t source_length, + uint8_t *target_data, uint64_t *target_length) { size_t i; uint32_t checksum; @@ -309,9 +309,9 @@ static uint64_t ups_decode(struct ups_data *data) } static enum patch_error ups_apply_patch( - const uint8_t *patchdata, size_t patchlength, - const uint8_t *sourcedata, size_t sourcelength, - uint8_t *targetdata, size_t *targetlength) + const uint8_t *patchdata, uint64_t patchlength, + const uint8_t *sourcedata, uint64_t sourcelength, + uint8_t *targetdata, uint64_t *targetlength) { size_t i; struct ups_data data; @@ -417,9 +417,9 @@ static enum patch_error ups_apply_patch( } static enum patch_error ips_apply_patch( - const uint8_t *patchdata, size_t patchlen, - const uint8_t *sourcedata, size_t sourcelength, - uint8_t *targetdata, size_t *targetlength) + const uint8_t *patchdata, uint64_t patchlen, + const uint8_t *sourcedata, uint64_t sourcelength, + uint8_t *targetdata, uint64_t *targetlength) { uint32_t offset = 5; @@ -431,7 +431,7 @@ static enum patch_error ips_apply_patch( patchdata[4] != 'H') return PATCH_PATCH_INVALID; - memcpy(targetdata, sourcedata, sourcelength); + memcpy(targetdata, sourcedata, (size_t)sourcelength); *targetlength = sourcelength; @@ -501,13 +501,13 @@ static enum patch_error ips_apply_patch( static bool apply_patch_content(uint8_t **buf, ssize_t *size, const char *patch_desc, const char *patch_path, - patch_func_t func, void *patch_data, ssize_t patch_size) + patch_func_t func, void *patch_data, int64_t patch_size) { enum patch_error err = PATCH_UNKNOWN; ssize_t ret_size = *size; uint8_t *ret_buf = *buf; - size_t target_size = ret_size * 4; /* Just to be sure. */ - uint8_t *patched_content = (uint8_t*)malloc(target_size); + uint64_t target_size = ret_size * 4; /* Just to be sure. */ + uint8_t *patched_content = (uint8_t*)malloc((size_t)target_size); RARCH_LOG("Found %s file in \"%s\", attempting to patch ...\n", patch_desc, patch_path); diff --git a/tasks/task_save.c b/tasks/task_save.c index bb944ea004..08bfbf947e 100644 --- a/tasks/task_save.c +++ b/tasks/task_save.c @@ -1370,7 +1370,7 @@ bool content_load_ram_file(unsigned slot) (unsigned)mem_info.size); rc = mem_info.size; } - memcpy(mem_info.data, buf, rc); + memcpy(mem_info.data, buf, (size_t)rc); } if (buf) From bd2b913bc666b58fb5d5b398072259a4ef69492b Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Fri, 13 Apr 2018 00:52:52 +0200 Subject: [PATCH 288/517] Silence more warnings --- deps/miniupnpc/minissdpc.c | 2 +- gfx/drivers_shader/shader_glsl.c | 4 ++-- libretro-common/include/streams/interface_stream.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps/miniupnpc/minissdpc.c b/deps/miniupnpc/minissdpc.c index a6544787ec..2332780d10 100644 --- a/deps/miniupnpc/minissdpc.c +++ b/deps/miniupnpc/minissdpc.c @@ -145,7 +145,7 @@ getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * err if(n<=0) break; \ bufferindex = 0; \ } \ - lcopy = MIN(l, (n - bufferindex)); \ + lcopy = (unsigned int)MIN(l, (n - bufferindex)); \ memcpy(p, buffer + bufferindex, lcopy); \ l -= lcopy; \ p += lcopy; \ diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index cdabb79ec4..ad12be1116 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -493,8 +493,8 @@ static void gl_glsl_strip_parameter_pragmas(char *source) static bool gl_glsl_load_source_path(struct video_shader_pass *pass, const char *path) { - int64_t len; - int nitems = pass ? filestream_read_file(path, + int64_t len = 0; + int64_t nitems = pass ? filestream_read_file(path, (void**)&pass->source.string.vertex, &len) : 0; if (nitems <= 0 || len <= 0) diff --git a/libretro-common/include/streams/interface_stream.h b/libretro-common/include/streams/interface_stream.h index 3332aa725b..8ab80737f1 100644 --- a/libretro-common/include/streams/interface_stream.h +++ b/libretro-common/include/streams/interface_stream.h @@ -48,7 +48,7 @@ typedef struct intfstream_info struct { uint8_t *data; - unsigned size; + uint64_t size; } buf; bool writable; } memory; From dc61e23a21e7ae69284294aa7d9d7eac4130db3e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 13 Apr 2018 01:10:10 +0200 Subject: [PATCH 289/517] Update libretro-common --- libretro-common/glsm/glsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretro-common/glsm/glsm.c b/libretro-common/glsm/glsm.c index ab8c5624e6..0f01a2706f 100644 --- a/libretro-common/glsm/glsm.c +++ b/libretro-common/glsm/glsm.c @@ -2207,7 +2207,7 @@ static bool glsm_state_ctx_init(void *data) #ifdef CORE hw_render.context_type = RETRO_HW_CONTEXT_OPENGL_CORE; hw_render.version_major = 3; - hw_render.version_minor = 1; + hw_render.version_minor = 3; #else hw_render.context_type = RETRO_HW_CONTEXT_OPENGL; #endif From f8b75482e9e0fdcb397e021c22334c16ab746c2d Mon Sep 17 00:00:00 2001 From: gingerbeardman Date: Fri, 13 Apr 2018 01:15:26 +0100 Subject: [PATCH 290/517] icon and banner update and refresh new icons: freeintv redrawn icons: 81, nekop2, np2kai tweaked icons: fceumm, mednafen_pce_fast, nestopia, o2em, picodrive new banners: freeintv tweaked banners: 81 all images have also been optimised --- pkg/ctr/assets/2048.png | Bin 1595 -> 497 bytes pkg/ctr/assets/2048_banner.png | Bin 24701 -> 8011 bytes pkg/ctr/assets/4do.png | Bin 1165 -> 563 bytes pkg/ctr/assets/4do_banner.png | Bin 16316 -> 6666 bytes pkg/ctr/assets/81.png | Bin 1671 -> 410 bytes pkg/ctr/assets/81_banner.png | Bin 15100 -> 6886 bytes pkg/ctr/assets/assets.7z | Bin 1085733 -> 369789 bytes pkg/ctr/assets/atari800.png | Bin 357 -> 346 bytes pkg/ctr/assets/atari800_banner.png | Bin 15963 -> 6969 bytes pkg/ctr/assets/default.png | Bin 876 -> 425 bytes pkg/ctr/assets/dosbox.png | Bin 311 -> 131 bytes pkg/ctr/assets/dosbox_banner.png | Bin 24468 -> 7618 bytes pkg/ctr/assets/fbalpha2012.png | Bin 1390 -> 435 bytes pkg/ctr/assets/fbalpha2012_banner.png | Bin 23032 -> 7312 bytes pkg/ctr/assets/fbalpha2012_cps1.png | Bin 2320 -> 560 bytes pkg/ctr/assets/fbalpha2012_cps1_banner.png | Bin 31783 -> 8480 bytes pkg/ctr/assets/fbalpha2012_cps2.png | Bin 3367 -> 735 bytes pkg/ctr/assets/fbalpha2012_cps2_banner.png | Bin 31430 -> 8773 bytes pkg/ctr/assets/fbalpha2012_cps3.png | Bin 1424 -> 556 bytes pkg/ctr/assets/fbalpha2012_cps3_banner.png | Bin 20645 -> 7369 bytes pkg/ctr/assets/fbalpha2012_neogeo.png | Bin 1560 -> 498 bytes pkg/ctr/assets/fbalpha2012_neogeo_banner.png | Bin 26102 -> 8276 bytes pkg/ctr/assets/fceumm.png | Bin 1302 -> 363 bytes pkg/ctr/assets/fceumm_banner.png | Bin 25137 -> 8041 bytes pkg/ctr/assets/fmsx.png | Bin 736 -> 268 bytes pkg/ctr/assets/fmsx_banner.png | Bin 30511 -> 9057 bytes pkg/ctr/assets/freeintv.png | Bin 0 -> 552 bytes pkg/ctr/assets/freeintv_banner.png | Bin 0 -> 6457 bytes pkg/ctr/assets/fuse.png | Bin 1139 -> 437 bytes pkg/ctr/assets/fuse_banner.png | Bin 14628 -> 5709 bytes pkg/ctr/assets/gambatte.png | Bin 943 -> 363 bytes pkg/ctr/assets/gambatte_banner.png | Bin 26769 -> 8493 bytes pkg/ctr/assets/genesis_plus_gx.png | Bin 2824 -> 800 bytes pkg/ctr/assets/genesis_plus_gx_banner.png | Bin 46311 -> 12388 bytes pkg/ctr/assets/gpsp.png | Bin 2017 -> 605 bytes pkg/ctr/assets/gpsp_banner.png | Bin 24996 -> 8015 bytes pkg/ctr/assets/gw.png | Bin 562 -> 389 bytes pkg/ctr/assets/gw_banner.png | Bin 13523 -> 5874 bytes pkg/ctr/assets/handy.png | Bin 880 -> 509 bytes pkg/ctr/assets/handy_banner.png | Bin 21298 -> 7110 bytes pkg/ctr/assets/libretro_banner.png | Bin 6710 -> 3154 bytes .../assets/libretro_neutral_shaded_banner.png | Bin 13743 -> 5279 bytes pkg/ctr/assets/mame2000.png | Bin 1873 -> 533 bytes pkg/ctr/assets/mame2000_banner.png | Bin 41669 -> 12563 bytes pkg/ctr/assets/mame2003.png | Bin 2134 -> 539 bytes pkg/ctr/assets/mame2003_banner.png | Bin 39246 -> 10319 bytes pkg/ctr/assets/mednafen_ngp.png | Bin 2100 -> 642 bytes pkg/ctr/assets/mednafen_ngp_banner.png | Bin 27754 -> 9289 bytes pkg/ctr/assets/mednafen_pce_fast.png | Bin 1357 -> 496 bytes pkg/ctr/assets/mednafen_pce_fast_banner.png | Bin 25219 -> 9129 bytes pkg/ctr/assets/mednafen_vb.png | Bin 1150 -> 416 bytes pkg/ctr/assets/mednafen_vb_banner.png | Bin 25852 -> 9222 bytes pkg/ctr/assets/mednafen_wswan.png | Bin 2027 -> 614 bytes pkg/ctr/assets/mednafen_wswan_banner.png | Bin 27072 -> 9245 bytes pkg/ctr/assets/mgba_banner.png | Bin 28426 -> 7827 bytes pkg/ctr/assets/nekop2.png | Bin 3795 -> 308 bytes pkg/ctr/assets/nekop2_banner.png | Bin 18686 -> 6714 bytes pkg/ctr/assets/nestopia.png | Bin 669 -> 342 bytes pkg/ctr/assets/nestopia_banner.png | Bin 27692 -> 7473 bytes pkg/ctr/assets/np2kai.png | Bin 3795 -> 308 bytes pkg/ctr/assets/np2kai_banner.png | Bin 19566 -> 6728 bytes pkg/ctr/assets/nxengine.png | Bin 7549 -> 694 bytes pkg/ctr/assets/nxengine_banner.png | Bin 26236 -> 8457 bytes pkg/ctr/assets/o2em.png | Bin 343 -> 349 bytes pkg/ctr/assets/o2em_banner.png | Bin 14771 -> 6018 bytes pkg/ctr/assets/pcsx_rearmed.png | Bin 2019 -> 601 bytes pkg/ctr/assets/pcsx_rearmed_banner.png | Bin 17619 -> 7699 bytes pkg/ctr/assets/picodrive.png | Bin 779 -> 326 bytes pkg/ctr/assets/picodrive_banner.png | Bin 18300 -> 7324 bytes pkg/ctr/assets/prosystem.png | Bin 1339 -> 465 bytes pkg/ctr/assets/prosystem_banner.png | Bin 15952 -> 6883 bytes pkg/ctr/assets/quicknes.png | Bin 1509 -> 496 bytes pkg/ctr/assets/quicknes_banner.png | Bin 26666 -> 10000 bytes pkg/ctr/assets/snes9x2002.png | Bin 2468 -> 766 bytes pkg/ctr/assets/snes9x2002_banner.png | Bin 45189 -> 11277 bytes pkg/ctr/assets/snes9x2005.png | Bin 2655 -> 712 bytes pkg/ctr/assets/snes9x2005_banner.png | Bin 37857 -> 10651 bytes pkg/ctr/assets/snes9x2005_plus.png | Bin 2832 -> 754 bytes pkg/ctr/assets/snes9x2005_plus_banner.png | Bin 47005 -> 10911 bytes pkg/ctr/assets/snes9x2010.png | Bin 2877 -> 712 bytes pkg/ctr/assets/snes9x2010_banner.png | Bin 34032 -> 10709 bytes pkg/ctr/assets/stella.png | Bin 874 -> 549 bytes pkg/ctr/assets/stella_banner.png | Bin 16250 -> 7093 bytes pkg/ctr/assets/vecx.png | Bin 639 -> 468 bytes pkg/ctr/assets/vecx_banner.png | Bin 15144 -> 7043 bytes pkg/ctr/assets/virtualjaguar.png | Bin 1113 -> 599 bytes pkg/ctr/assets/virtualjaguar_banner.png | Bin 20582 -> 7688 bytes pkg/ctr/assets/yabause.png | Bin 1264 -> 639 bytes pkg/ctr/assets/yabause_banner.png | Bin 17642 -> 7190 bytes 89 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 pkg/ctr/assets/freeintv.png create mode 100644 pkg/ctr/assets/freeintv_banner.png diff --git a/pkg/ctr/assets/2048.png b/pkg/ctr/assets/2048.png index 04b79d3d21771386e158905740b54ae1eedab20f..06e4cc3da79d52a93c81d3cf773269b9b28a6f32 100644 GIT binary patch delta 482 zcmV<80UiFk4Dkby8Gi!+003az3AF$K0ANr|R7Jh6obTt}&cn6O$iAAAhJ<}{es^uX zv!srPdc?Y|b8TaIa%aoHvg+d5+R(zbtevf-n4_DH$iTSZ*w63d&iAr=*`jrqe`$qw zWzU^+<*0XZY+}Qfajc4M@~?T&opZCZ4r~Ab0Zd6mK~#7F&41I;lA9nDfMGQV_#f36 zSEP2e@Bfy~FugExI^*7K_P^$nIReGctW+wBO6j}7&D7T1&cJzVO{0N^RPzcN0kCZj zI*adbHTD%4WVpkagI;))rGx1O2&Sq$wKhZ7mE|3}BWTjc4dfs-Sxk8Z4(?SoLZgDI zM*0^psNk@kM}K*Jhsmb(3NX;-psSHvc>*n@zhsr6M;IT^x&ugS+eR=sm`I(^VE_?m za^Q4O-39FMmD}1n_qO+N1$sh|T-1{RiejVvk{ z_?>~s=R9vZ=!H?+Vc#R020Vkyz#0*rNC5->Ljeoupj(Gt0Shq%b!dZlUxOcxV1+;P z{0XZ9{xu#!fRGL&WT17=47lpF!#2+g?CQZOGi(rJQWwF#39#*7xUKsd_BXzVi{OX# Y7k8#dnpSy1+W-In07*qoM6N<$f_8-KT>t<8 delta 1589 zcmV-52Fm&I1G@~68Gi-<00374`G)`i00v@9M??Vs0RI60puMM)00009a7bBm000XU z000XU0RWnu7ytkO2XskIMF-#s91}S=Wfl(T0000*X;fHrLvL+uWo~o;00000LsCUS zR8LYmAZKJPYH(#|Aa*e>Fd!&(b7^j8AW2F`AWBe0M<8}MFn=j5AaQkJY-x0PAUz;C zFbb-S#SQ=f1$RkAK~!ko?U>t7R96(nf9EpH05c4zGmHWf0V|4DMFAyZif;r4PprphjWlkVwNy zCfR47lRfL(>wnwd+G`1oXMWO|$0jn*ZQcWrr#@6}-C^qD%{Q+Ckmp)z>be2nzygKd zO$xoqdU|{K>((uNLp~HGfy?b?OU)KaOG?smAUHP8z5D;*^N%2k0v@-E4eKh%%*-?o zz}=P>&YeFuZEm8gi>|IN_U+lr+I4GFay}FebLr}J5`Xb{a;~$xmm!~@Pd?g-EX(Or zNKsW9uQmdZ?{;(Ot3w?8@*sJ+c>r9#)`+gBmP*>%ACZX1vB(y7)z`7Tw#L|;P>4rQ zJIpx)BmNOoRRN%`ZW{&5mQhq#NY&=e#x71w5DJG=0?_-+3qWykA^9#BMTG@;^4$RR z^?A*qkbm3l;@Ht+M59q;%jD;wP{`N;QNU)k&f58KBtkr9P{*0$NY3Xuoje~LVl+60 zuImVbV2%peY&K-c01ylYxzlnNfYQ>k8FBSxi$tQyafZc`99uGswNybH*nF9yDptPhktpsMz&PEVG*C7;|RI!QBMDo2PiRPSv3l}dE7!3fh zW`E5ZR7K%(ws!r(b2(NnaAU% zwx)^D>2pX zwY78UN)rG-e1AAC2Ri*Rnz};_g%u=0z~hkFQDVpAu*?mBU~rsEmz#*EaDjW-9)J3x zoM;)HUV}$e9uCDgT)mh=r^Q?f>AKFZ7p|D%Jizar#?`qX$HLKVE{<$nLatRp1Dd)+ z=AVJv&8_tG_NUxNd8OkKF$$Rt9vjX~3AbHlmCI-ljK@@S0Sx;CG+w`nAc#~|zHbVE zf)^BdIub+CbfStzL@wBTLL^9j`QQS zAyi#Q(V-;I!iS}H^CEEb)*ZY9LwNFC)Yh1n>?A=TE5mRhrWwuySY>g>G<_?8=Yu}3 z|M54HD6sd_T}YyNspC}ZD4in-Ja(CHsu%P1<~$ZttYqm|TVHYCse*^F@9jOE#yq}(!00000NkvXXu0mjfTV>^S diff --git a/pkg/ctr/assets/2048_banner.png b/pkg/ctr/assets/2048_banner.png index 461bdb396dc220361a19c369111d13fd0b321649..c110b894e629315d8faa5f61941f9d79d412f73e 100644 GIT binary patch literal 8011 zcmYjWWmHrFx4i=c(hP`n3@9nmDK&ISN=YLK3ZlR`bPOR9(jW}oAvH?(NOwvjpoDaa zh{U}4o^O5YogaJcv(G+z?z;J7BK39E$w?SV001D@)KD=501%D>NFsb3PPwQ{0RY~) z{v#vR($Z2a7Tep~+tbrCK0e<6zco5KIxsLWI5;>pHHAam6o(TN6F6~XWTdmR6Ni0$ zePd%|larIf!^1c~ZVxBoY(qmsI6qFrwYb9RxG7G=?c)46{a*-9!4O@YybOojCM|zoRnq^MTt}ZT|>>Sb(k}1i_xC8cZa}(l!Ajtm! z7bPzvd!L&}_>NF<$yu41^UeeRc{Z-!80tR8!vhl%k&uy7La1)j(9+T~FfcMPk+8BsIXLd!k>N*cR(hrNq@gDtKoXhgSP9~H-bKKv6scQKnGg3U|Jy^gmWNu57Ru70r-n^7}_ z`ZJZiF@nXSC#nl@S2nMt>}L^LDRpkNeDd%5llr#msSE5$Pb8|TX7zieUtPzivZ?XG zmamK3ZH-)_y)T;I6pSAvpzD3UE2J*H8JyZYWByD-~8gc zrJ1n4qQ;BgijAl+r38KeU?S91Q8e-feVtW|U6-POO<)zWE~_rtKD&5|!rn?fzuU%> zOS$R`)wv%G<`3soCF-ZfBZ?}|k7$g_MfNW~AkDmV{MO=qfOk2&f_9^Pd&j{53^veL zdSjeycJGxywWpcGdAqbg%MiZUMzFz-4ai|)r;9qfFI?B7Qz@S3aK2ZxYCsu4hr z#NMD=8p5=GV;ectpr{X4R3x8vq@U(vsWSrkvjNkFKv&+go9)N1~lOq2cW#WjKV_;6v2eQA;Lg+~f1iZ8P9#0(5}XGn#B z@4OzSk>*S<3grHH@vt;K-;ObDeWxCJA|nugt33A+7sK<2Mv|azYKT>NecWr zME4f5N2ZY$w@l0{I4q<@MF?8ws;w zATcLOAsD>H&wF;?VkjRhqQ(X}FnEoY5ztkVk14uR)LeX3Q0VLW&gSOmJ$J9go{Kj> z3bLJkBk4RYk8g|jXmT408!Jwz0zPE~wj1aJirp`7;Sb?+o>ounn#ei&vE4=`6Ok3g zj3WaTsAa(cX4S_krYdF!VQknt>(q)6)dzO@N;+)NpiUBo-gAB&5d-j%hX%tgmU zZ4mPg4<(W#O7Wyf^3xqR*5O$)6(Zu({tvS6JMvZL-ruX*#q;ZX&|aClJmP0A&oEx5ja~$ctQ6>r9i@DdOW& zl7Sx9lQCOT)V#YqLAlC+Ngre(g0A+hm%v2~eDKc$Uxj+8RO0C@pq{_i66E#|h~-a- zb)#30SC6?N5du+oX&+JwHXgwP0^~}x7c5>(i;c|sZ_IAh6cy!dB%eHJB%XXzl>9g* zX2!B(|0ZeSvQ$T|xVjqATieR_IPz<92eL8J5dEBS3Ql7^wNy~HIQ-}kt}vtUB=F~% zcud)XnM30P(yLo!0F`ZJ{+r?a&2mln$L)P>Sf!(HdyloF#4}Wa{YPlnR@4j>mE!zJ zKIhtMdZ$Nw)p5bfr8!&SJ&k!@!^%0p@iqTNR9SU<+5GTODs02dIq8Y%MQxu3L+XYn zC>QJ0E0DWrQZF8hJ%0|4-S6gw`=S1?80ai}yZ-a49nQ=y&Xf|?OrJIXFvIi7`TV$g zDM94ox2?$Er>p!1ZDmJ9!2_}V)F9EMzy=7267~l>2wQYviV%+I<}+zziA*+-`u13u z5OOD)>TX?SdUrV(tSSO3r0H6aVJV>rS%j&uvuZ&1(j#ndZ=+q*k`f@%Jp5dav#oQ) z;x8?lMZNkmAJ55dA}tkW5Uku9l0mgb_B0%Z0UH*C=^!5v@?iG}Lr#+GA0X=V1!yt6` zX%yJZ#PwPHJPP;Da>;N+e#8!R+@-GtIPR%znZRDP8^4&P0%20krD#=ILA&)r5!Uw# z3kPjkQR?zU7!MTMf|`ZUuQV9wqbFRbC4oJ9MX4XM!}6g`6nkunzm{CkfB&|sb-(Q{ z8^^u+uMA4CVseam!kycmL*oQkBkA=}w|r^v9&th%xYD zOvXB(diqe+y_Ui1%ReytHClE{iWdRblk8W%l1ZO9(r1i`^P_Ty$5NS=bk zJ8q_r{PQA!%nbhO4Z=v%SL`)!2pHCb$qTPyDTRFE^wboj^E-63(QQUsiNr^rWOts5 zj1hfW>PRp;TYkr>Xa4ZJ_IU?)Y-U`x=s84ko)!9r^Spaq()+rd2qp7`I$qSg_w$e6 zd}W-RNW^)<_a!TUXLon+z227XZ5* zcs?>LqVprBs~oXlX31ao?lly#KUEGbaTfX7Hy0rA0gWb5E;f%r3{`!v_-*ktIe{h6 ze)%^*3z`7v36832^ttA-QzSsE`sxoEyW?VNSAO~Wl5CaDy@sxeEbNmtvb8d9!#Tx* z#@(Ko2gjXsy`DD-A8JNis1O}ABFF4w#tz*PilImcY*8}az_%W5#=OfAnP7txG3l|! z_eQGBx)t&4lQc~~Jw#%AJ%ZJXPY-@nP3X177|oT5Ys45WShpC`serku6Hc>tp9(*x zW?Q_iuVxqjW+(a&5Eh&|6L5xbaEm&0HI>guwRD_zEXvauDS=HN3JUX${58Hyl+awO?3s0=m}5nE|_4&JD8L`+`!L@7;?SU@|263m4iW?i%7n(mR^ zUo7CEf%txTWl@EG(Y>tZ8^?PG|BpEEBQeJ$_wm(}ejf+#iq84~hJbH%ABJTpIgf|m z^>dE21T-(hN5NKuG+wT{p~JgkWet|QVY>6~zrqhBN{#OESu2=xrtE>#AVB#eVs0#&`f!>Zgz6k1>ex+ZTIC^4j8+J{S9(Ul3@qObXJ`y1c;28Gfz$V zB6;pbakQEU77|IeC$oaapUyre#>)?@GDlVU@&b@Azu(Y_JT7^1p}--m%;5fW13Sk6 zWS%z#;m0pWxihKu<@!bHZL+LoXjV>;N*3(u%Fw{vILJ6$SaWyPfC&>}jI9;^Q3POC zYL7%D(H!{3g!Pzhni(Ti9(Vt360x~w>g>YvUN=N(T5;;6nFa=nkgIb-;*wsrT&>DB3PvG^JCVH zFFiwBv$=tQ+|aklPDB032xnM5;fWa3f$T*d(8~-syZ3?Uzw~V4E%`q1sVo7{Kqyv~ zzB=e z?-sO(gY^?_wU9(naUx&%G#+sDRlXOB^Svc)^JM5iiw~V#<-G9(Q)A<;FCT>QcTrFp zB~?w$^pv*Lc{(Oh`B0JbDH-4nR^;GUvp29sr)=-sduANQ7<~)Vg$^+B89vYA{p;*E zZ-{D)=w@x@CVAETzPrP}IX!#$(EMFfwPZFiixzomw)5&&CV+~Fh<>%!=IIpd21)~I z?xeO#(FJ2P%wqqxUM95lhpA1yD-#}nR!9-@UXNoupxOE))JAcWGn|NfhP_OA1^@nG zpG&c%Ea)OqTkTQ$QhfYUHe{s9H+Aus-B|?fjgwd~X!nr6l7%vpx=c%joS0W#oVqj8 zGZ1D^1=?j5Q_C;dCV9)8=Tas|fxYTC{0kw82@D-BZbR3$+x(#dZ&r3_XIoF@DSu^$ z?@ZO%m%ij?TCY!0{#7bTB1%#hFYVpiiLo7Y*8??cOVIYC^jJBsO9eyBKf!m}Sg zKXUElMHyU29T8Vm!+$%_E5nK z7nHJINzk#ZAM?#0;~v>4ESY%Yvx^o=Za79z8FRevpDO6uf}BN$Zj>8uPF{JT#|q|6 zBAX6~dBi?L7H`|M-T~%Q^JHhtgKm1=H|H@jv00J#vGIpDX?$2^g8rB;-q%LM?vS2# z+|!w1?d|S?Wk_thxw&XJhK06v@qD3l-~3Q@$e$G0V%rUk3w(FS?gO^=mx-)@U~61F z@gmG9!;#*UTo`|WRr(c^wxtk7P;Ff2mmfZNyzMv#*eK6pUtJVZ{Iw)6{xB+HcAD!$ z;C*}xX-t`^)eG{DL(bnybC1^3?pwIkHI8N?gFjmGuACM*%HmwXvY-a2kD6A5=k$Kk ztxf@6ClM_Kpt*MF;0=>m{k)5mSy+hs-0oufcVf7ewoYILmKfN}&cQ>Uwx72rpgfGE-e@m7?S}?kRg9q-pGBpyTeSaa`-q^CV9YA9mXzVa z{)MyvgxUjXWpQ#Ry3bQJuh%eqTPQ#~V&87YM{LoJ-h{VMBSJN*WfSq!Ts?0Mkx6M4 z^r3hzf56@ez<*ACNR~~0+4&B6M&bB3efAY-K@Y?VqU_j9sQnSueqQciP9=T6LW288 zf}j%*7e~!Wx!FMs`X$!cfV6de+u^AbA)~gupN$pJ_<}E5=STaYDJvDeEzFXtE_Ams zKc&3xl5KdAz-?{tL+>$m`&FOr(=i7D0Z~y=_;(Q;{-@{!M6E3yi@*B8UwiDoPcchu z8Eg2w_jP#fxqG0YX&pMCXM#AkTrl()*hOsDnmc*%%Gn!eDzN~xf*RVgEY;=iqP>C? zYSUsla5Hn;oR$%i&SmL)^%r(u`5B$fIN5{Q;~#lAaf6_k0y2ENor&`6<@EwYWN#0M zj(OZ+746pWr0XGPshO_jxPB#jVSV@vRU#=dgDo|b8VUpt5J2&Rr7~De9%sd#mkU3K zgGU|d&eSKMtX+yRchcF1rpSJrDOWQRrVZO7CRS&G#Q*(6HC%8DLFSVj*4IO-_HkhA z*D^w!AWT}^;%@ky3w^^>)LAc{$JE?0Q@US@DFyCR*5=^NNN6>WyzzGk1FP;dU-;A(1oMd!d@iu(B?29|8 z3hY}ET-hOr$zz7)2Zy`M8e8?zzp%>sJhUm5*aUFWz!X;r6K~~_hZ)K$RB+uczB4W6 z)SGCh`=zltY2_K=)vpb0Y)uDN6ncHIc6fR(b!&y_T;?Y5_tD9SO&SOs4oe;`XD$!<%BNRlsHoUR+`o+Lo4d%G^<_uC z6WFr4+Pv?vJ@cCD&&@gLd0=YgymGa7FO*^&u)2<;n|ip(m)%!OND(<*1J4i*f zUr8NL;!LNeYV=D=ID{WC#;+@tDz6-(sQZO5-!N9J0(5vuv=_H@_V^P+*@@rd!f>$GE6?{q5_3sN?M z)i}0B>D1)wT7hbfi^$hweJRX5!SP!Ia^A@pujwC~Tt-EjnaR%SYsX=)XoZUHjNB1= zx2jArV04ti*ikOu?+oF%_Fe>#josV_1dk#7e5_r(^>PE>jcooUtP-U1{JBZDxp>h~kV##lWpCG=k5K?l{dh1gPPU$)j^ocjkjz`kOW|C$z{14w-KE(LY()m}U({?XM>Nzk0aAXz-X zm&^I;$K#^6jH#9xo6TVl)4tUnZu@f|RI)vY za6~p3WVB}_^u>MbEz7;-(WqfN+cS4u{qL`Qu!rk*ck>Us)sJ4gyJFPuFY*K;-fK zuj%_-ym9N0^}H;5x3=2afP?R^BV^C?9aXZe*5g_sO#p9irv)$lW zTQ_2urD6p&X!{SBakx`u&zm)$Ye%0Ggq|)%TiqJMrR*&PdJQ|ZS{D6DAm9J_bPzurKLdd(?yVC zND~1A6ka3WP9TY378)K=wjXZ#q!Is)4N#K|KI=gsEil>wNbq>9x*N^++z^L~&MQX$ z3%WYy2X?;+Y`Rm7e^D0_HTQtG@|;GS!|qE{||kJN+Bmq>x4^m?(I?as7!) zy^CE!{N1MLEaX;~wgBLod=uYf>2jFjO4ZcTnmiJSQ20}V=}_2~>1Yi5>2>av0}G^N z+hDOpotj>zf^rU6@Y{#q@%A)$87@{WX&=R|UlW|Mgh?Q~W9_WbP=oho$kn_s=6nJN zLPvTLpxbyq=&svJ-_+FQQNiJ#lFX|kzf;d3B9H9eQEd#F;*9uG*)!}Hssy! z@3*bf#%{G=wfPX@E&n*w)5tmP^7jn6%41AshKNkO&SH)tO^r%zCxqy_*OKbwb0=x) zQoqtowBRxDK04bNqj(n5F5J!P#fL@{ivojwNAF3FLKQBW;^lL|6{%!gnXdky*GGTv zl?wuN2TDvDxnO7r8D<{(#3&W_UfHs@Qi!iZ_x>Cqk((!)GpFxIxxV6lA~vih7tlMtq` zL%UAKFL}M&SSQt zm>^iJ|K4-Shv(qlN(BC^U>j+rRU9>#v>My5Uc=Rwx0#Q+(H zFLr*3rSMVJz8SsMVgi2Yh29CG+`Y0oH}R^8_%-n9C}DwmF&xTRQf69iIQ#9}Vw>r= zj2117ll@JmtK1M~nVa^-*006I*(N^IpIdy_PcUaQ->wBFZ*E@eOFYau{TeBcm7U3+ zv9>3Vucnbs{JL>1hn+t5Fin{pi_4RZS}a&v%bq=zuXFPGutK@%a`t$N_q7$m^!2%~ zAknI*efV{Uqf3RV)<$7eI~`y1!MfbxbXy7B%FCEx)!x3|Zqp~Nm)=;$c!MvXvV00* zbuil2ynK2ow-q4BM-VVf_EJ=$MlI%T)whq6=jAO`E#+0^2V+maK7+__a^uZapq?oi zc~~h-HZaIvJg97-DMIFeA&qh}Y;C`n7N*4X#$333 z`z4!$NmGmtG9_QXF5*}D`<}e!TE1Ak)?wYGOlI4nZ29lSj`2Zb)$;OVW0G;f=aA?- zSpQMqgR+&8PeH@hHV0yw(%VawyK*c992aCvju^%lEh5Iu27d#!ngqV(z?OKQ4YGCc zGl~sS`&bwHIP@x$4L1-!O%5x7TYF&#vDhnh{XlW@^t3%znYFb&>FnB9tBUQTZcy5W zolV_YUgHxs$nWTBMu?f~;S`*;O73va8?(E}vaaQ8rQYNmnfr%;A$=y~*#>LCx>b$O zt(7YhObm(p&cly`d`fA;uHm0Pjo7cDoObc;?b}0Vt4wMIHFw~z;)gQ~V?{_EB9JF% zJ0(TNhHD+ehji%A;c9=%9G!&w&+k6xOPH}a#r^><@8b`fA>SKp>>5XCsoYhBp^kzu X;_w>QU_tBu{8z52s;g3^WE=87wVT={ literal 24701 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¬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 diff --git a/pkg/ctr/assets/4do.png b/pkg/ctr/assets/4do.png index 296f31bc511227db0957ef37ea3bcd041dc3f8d2..7eab685bfbd0ea31110377a05fcec30ada38f035 100644 GIT binary patch delta 539 zcmV+$0_6RT39|%{BYyy3P)t-sFfcG7At4qP76k^DIXN#cFEcYUM{rCH4Gl0b zFfJ}GTU%Q#EiExIF-}fSv$C>hXJ;=jFME4?z`($yq@>Nw&2w{eMMXt5H8qEahnbm~ zt*x!QyStE(kbr=IBfRzn0000JbW%=J|NsC0|NN380RPFL|9|#F|NsAMJv;mh0004R zNklpm?96uW%cleuA=BRnsWW#W~CL4JJt z%)Iif5M0c@aKtO;>R1F^USuy^`051&mq*BT5U$S=rf~T7{S4$qfiPWGU_bUio(V+M zB(=opV++LVfq$m0Ayuc$p1`aSh%?&uv=|I_qQDh7(q_`fapkz;=+Ia!Zt69$Dq%wC zi1i5rz!7UO6YyF5>P>9G%pX9GQw4^KSdEaeB?~4e4rqiJnxw9W;jR23YJ@4%)5=LK zx_yk^B4Y4PO1qItrw~&GR%vlz>7A)8r{x6+EX~Fl&3_E;>E)l7z%qpTA0s5JL8{m; zbqK23#snL*L6~jZNx~X?e6DYgwlq~U(bOnxbmv^B5vAoZ3ulq!9qGszP0S!cO{_zL zftY(57!ud~Y}JaGMhf>}3HfhV=tkm>z{0-QZmunE@U_+IN?Rll0?*{j5RbebCq#jV d3W0P#0TgwZDO9^UTqXbj002ovPDHLkV1kDl_B8+i delta 1145 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 b4lp>C-%&`Udw?bOhc-Y>MH^bBWF7Wj7pWNc literal 16316 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
Kvp-i30g8>c&2WGTFxpQP}?>^yd}oIiZ{bbN-q3 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 Il&!=57eR;dkT>wOs1;lzM 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%0Av>iQbiMz0EO2sz5<5$l6sA(NjnxIriib1 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 literal 1085733 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%0Dn*UK5A)@O4N}gaI-NJ}V04}^TLOgzL%Uc0w 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

1O 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%0DZFw$b*H+n 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


WjUHo%MT2iPjh9?Bgw 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

pGd!lXa@J{KaJHJRR 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
  • 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

    Rp1MqEr<$QZWainy-raUlQ&YnTVV`TmD8@iKZ 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%0DtnkkDWhxWaGD-CFk^Gtq&Swdpm$oRcX_f^9mJY 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በpOTftc*@fMv0 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�L)8lrZEkj3VA(QuA6>k;%_X$ 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 CrTifP diff --git a/pkg/ctr/assets/atari800.png b/pkg/ctr/assets/atari800.png index 05796ded0da7e51af7e88e2a95ed27e1ef64a37e..501edde06b3371dd8d9eccf769799a6ea7b0015d 100644 GIT binary patch delta 299 zcmV+`0o4BG0@?zQBo|;%OjJeEw39L`BXvs=f>RS!HVkh}7m+>~e~VNU&WjY?loRTm z6!oPQ_N5h|YYuc+71f*rk(M0Ht{VWmZP8mwdgVgvhImH7z9`;ycCw006Z~ zL_t(|0mac(0)sFBgJIm=o%^41?n|c7e(yiDjdG=G`krw|-_ukD*#*SAz_^eem^xvS zz%n(+v`Ao6Bye00e{vlXxPA} x4ofZF4?k&PDHLkV1hhQfaU-I delta 330 zcmV-Q0k!_x0_6gbB!75NOjJeEw39L`BXvs=f>RS!HVke^6MRn+N;MmrgKK3)AG2;8 zzjPbQdmYq)9@T*!j8zniR1{@IA8}C}&WjY?loRTm6!oPQ_N5h~Z4RJo4scN%bXXPD zoCD;i0P(Q^{kj1Dx&Wnl0CQLsWmZP8mwdgVgvhImH7zAo%zydo0001}Nkl*^@V`$ixb zAx46Bp{ig+z*Yu~2}CLZ{W=Sl$I@!0c&5?=J8)&C$6bQO7ElUf0_3_GEgbzwrSD_^ zj$H%ih))`$NkfBS=qHUKR6k(Wa)UQ%u;I|aL3+}-g;j&vKO7ONiYq8p8L68^8Pwsd cn*)(F0O?nL&FXc&O8@`>07*qoM6N<$f^LY7+W-In diff --git a/pkg/ctr/assets/atari800_banner.png b/pkg/ctr/assets/atari800_banner.png index 0b731557de496eed3a1052dbe4aeb7d8870acc06..4f93af10e38cd738210ed8645cf89d55f9dceb81 100644 GIT binary patch literal 6969 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 SDgWO;V`@s;ina3a@c#ocvOk6Z literal 15963 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

  • ; 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 diff --git a/pkg/ctr/assets/default.png b/pkg/ctr/assets/default.png index b6c6d67f6c76631e272775baca7f2605eb29476c..54f3fb4c2839658b349adce8513c2bfb8b12ecb0 100644 GIT binary patch delta 410 zcmV;L0cHN|2B`y(8Gir(0044nrdt320dPq~K~#7F?35wwt1uA8{}Nk0iA2{Y$VzN| z>NVcru=SZD!Qn*{%p?jifx}f*HThmkfo(is^WmrNZg#6aiyEz%3;*@*tKqrD_XMZarzMS)9(XDOn)zq*K zIIR7!M(W!QDCdG4hIz$34zR-OXwi}|XN}&E#tcj$geVOblyG!_^m!8gOQCZWO&1H{$Q%b%0V}Gkut2gUn2EQZy@Q;ZnPSGJt5;p^x5Ef_DB=EPA;}k`<&${^{Y5QzR zE>mU2#->41%pA+v43V^+X3;+NC07*qoM6N<$ Ef?UJB8vpDxx^PKcnSYdx?o3Ro0b`6LXowgo zphApHYSGe`PT%LEw~zjr&a@C>Oy+kNFZY~#&+p!I&OIkDLhdPY&>(0KGzk9p1o>LZ zhzDum79&hm)o-DNX-1f=Nnx#V(DPpQEpt_Ldpzwm2QAjLP=_G#JyZYmr6rm!zf`~W zk=o|#5#R- z>}GqAjPnIILS&vI<_FHskHG=QtavoAHhId_=`d1fF_THAokJzeD_*ld?C$e7^+bqU zNYlq@W|DJ(vx8kkrY^FEJuJ9|+M}O<>G%eB#T;<2GY@|#c68Zt?qH9qVDbYfX8A?^}&`b$|bri#m z`h%reY-j5%!2|59dcqdor%Z(0A51L7A-YMC!ZQ0?d5rG>pL4={gAdJ{hfCJz%#%v;iS7v`ajl+jupt%-ei z)xWw!@msc|C49rxkl7K>unmB7oT>5(I(U`>aFfp%4SuhL8oZ*8*6y&X7u;7>ztNMb z{^j92X*l=!krlP<%gb)=@+(!(x@WZnO}^_bbJXh-*#Npk8Q5g z5?Nodtm=?$D<^otD^5A(=XTWXxv@tabJB5-yP=9cT3RY|^wLfWoM&VOskxrLBt%T{ qZPvBt7xl(JT7#fL&>;Bl3H}AkA`WCOiNst0000095|2?W>pAeS$MiQhEy;nOUO9%H6CQ} zkTkaNU|5+llSNKpszUMswnY*uGY*RIOGotas-){2)?vJMJmY}D3+|D#XqMn0^fq~&hLf!=+#aZAHSp7!q;#?G;150}33iiKkCm zSh>HxaOK7lMi*<7pZiZbe)PO@4XCgo-tv%S+O^}WqHaIa3X1!uafP|}vB9qRQ;JWM zZ|cr`xk@-~QqA^|tM4m!q#9k$nw%c~Q#j?f@yFW&_wqF5|9<8BInyZLH|*V!E4QAU p{{Ky0(P*>b&9-_DpwSKwSnf|nOhijIq7^IA zmW61+OtfYqnllm|E^$Vp1tZa#m1xC6w7SHZiI&j6WGlwMuQdzNlKC(GSLYIk60I)P zF0)KTn@jw1`!8kvfB4_jKaoqF%j{qLGX5*MME-97tNK6uKl1;B|I7c62_>dG_844k z)Pk;={O5rM-&cxTnZCHVaJa9@%*-sr0lmuwH57nG$wD)g{?ojSOj|48PmYc_Ij-Km z4t*&F-FfoQ$)y3G+r%t)+}GB9?4Pru=Zp zCKeSIvNo&^O%{Dp+UJjQhz6J#jJBrySl3KnTumn zS>bPLE`m*XMSam&6iY zk$wS1xO2jORS>FyKp>tFk8$y#;Sr~&7f~_z*ti7z5e}d9x*NZjnnu9mGu{&k7pIx% z-@EwioV-0ekyuc4c=54xqP+6By7r7vf85x7)Y9I$c>KA$w-47pI7A#C8(*(%FRhy# z8DIFZG_kTWeN5cn!hSp2U%+CH|D4VY^z6?OH|G~IXJ-o=$Lo2A+udpRYJi^rfGd7T zRV71T;J0)GpE6Nu74MPl;C^GQggW1uFBJ-~FEKp!(2g~hz_O(*s@Cb6@}x%7@8>h; zSdAi#cGb^?tEuGCBb)(QEp>-XF1}&tv2J;(p42L0nxbNi|3Dr#GI66bo}qvGKus9} z8aB*|ALls?Iwwk^{_H;XTy_K{0ICf66eZvPvRpsfCF>|NZ*9A#DMQVMOF=KAQ+AD8 z+06Gp$oNNfpq0|etH2)nX(-wC(Dn(4?i_gBtyEI!4!6fu(R7!XA03dJG(-k~!foK^ z%D31nWIP`!XJ`>L!5;~>J};;OMgW4fKJ(QGd1+d3WX-eyzgcG=pVFB+mG5RurB6Rf zfv58ds{Dt;#);K;1;~i+vc?M#hRQrp)smXLVRN~%t{NMoGtzQ^CY=RFgUfZ{>JSG& zAEYEwaZ5$s!v|Gx{agLHp1b$EY&7|{LzWgGmMYd)KlGq!7OM-;I=H>sA@9Oryt`+| z4hU1&pT|kc(=!e8T@RlCogJ+|d(j}vVBA1<3uru}MsqhcmN6n}RP6MacR(zEF|31o z{P;=;x{##s)fKf)0kTYrv-RFu*G)tN@^t6SI%-wzyKT1;iO+9?C}Ee9Gr`3<<$Fs0-DQDD&n zb3`=>_zVe_K*Q<-R*@cb01Kf~m#5N}2KBta0i1-Z-pBsP(&^j^F*U>QAAE_NN^j5U zmejhz`FJ z!8ZLTbQP^(E8Apr5@KK*a=lV6`XQRn3;7;ZQXKPI5LFM>&)Zq!yMc1_VT0D40jqhk zqr`a4k%bIN-Oe5yYgc6$RAC}QO!69zJoJ`R%r(VT;QIe+>PcZKmbrli@?=5d}E^>+u54Qs2j5se_0+awxB z)M&|jZ^=LGJ({6(%HSwX`fBuhQDlp1ApO3Yi!h02r-7}CJH~t14#9In2gUGWe{~!z zMz}ke6kt=ZRsbFh^XkeSjS{Q`*DzI)vF<%i=CWtV5e=L+W-ixtHv4lnQ#?nhFd8y- zj}e77KWQ*Q^fa3GFf*ie)UIGtTM?{`fY0|0gr9$n1lxI>qr|hnKzQ12)CAS0f!Vn8 zO3HiVdK}M}+Kc&Dw1+KwPWP@P-c?+J$9E15eromdf|2yF4s+{8(2iIDW8(XmyE-wy zf6#FAV46dhQM1sb={o*pa~=BezHFeo`OrIO4C-`v)?JmI=TpXa`Ucs6t6qpur)D;J(!5LGjFACjy_65 z{ixg&toc#EnA(UPo^Jp&k;gx$&luqCQZGXli`?N}VR;qp>9oNTGuGkYdJ3$kgeWm2 zq;OPu(#4dse-T|YqChEOasGroMl1KT!WnJj?nM9%Qw){vz3|1@P{)bFhpr2ua6ucR zYnez~2)%=nrBi%3$EJwE$Ax;DX6b-6NlGL6&Rv;91@glOxxq)emOoX)4J5y|aDk#h z3gq0d{Gugc-iBLC$qjq`IqLz*0AQlpHkqE^Ym)R7E$XE^QY2{#bcWw|L_DqbR>y-? zGJpJhGX_}ee^p8tfCmC>MPj&M$lINb>f_Pu2$&}h*yMZT`X1J}2cmk**7aHy0RU4& zH9V%c@+5=Ze&kr3+H_wW<@5#>t2rMJizUZMl~F`T(6b`=NcL6^&<8p&-%zF2RW!;C z)ivWd6IZvVmH;%xRU)c2%knVvVwWE-$Q46Gz{@0a#$;PKz3K`nj9bYRymF&~4sbm# zzw71XgLFuLjW@vk_zhAss5d=cd!nVwBVfVpm{!2ECO3dg9nM<2w%bdtzAK8V$`@K{ z*>kGL4`>W6uNB=es>|usSJJE`zhSyI=#jL=B5-QHd;w9HqZ6~vOUtLYC8?&F`tn>G zHzTcR+LN456Af^SSZ$GK;6d^&!>;);P^doR-IWsyb=XC)+&*VY*QTSA@$ios4E}zn z@mgsqrrLV=p>H7&o(Bj``A!QRii4P=mYl)uQ4C|zN{CjqGE|a?Zh^apG_{%6{3B~p zIICK83bduL+Y-aX&9e9!`#K%X#K77S&Fass%>yLsSWa|RyjUkd%;lUNKA-wcQS^i( zPeBR8n_!Q54jAbY-O6k{#2T{K!`gMr9CWqS9o>uZKE2@NwD(=GN2t1<@-phgE~xfk z!jOF3vq*gyw{O-7EKQUvb{pOT>~Ru*Y~_p{Io+{(wXBE*c6RUv7zbi=e9)}5Pp17phO7ms6d(z9}N_ZvYxf_kyaXYGJKG5 z;ktclFwY8VIJ!D)TP62&bZ=GPN_fjJF?Rhh?aM{Yjq4(|XV2B@K8#+4d@}8ihi>-* zEt-iT8SO1$v)_V6ClmR_4jPOVS(@E(BKGH8z+K`yAF%WBN!XNxP^`uExC_9sSHy^vt=i#r&;~VnEzFv{quZ?3c%Uu0k%$8 zK`%W^k~w8DeLm+-O9zj7{uZ&QXbKlYSfe>@xWE@$9{0Ml4 zclO4lQ_nx4D(-TlFDLLLX6RPu{!f9O%R@g)hf@9BI8D5Fg*OxrZ>IK1(SALV3 zmw1^;&7O$(l0?VTPGM_@9TYYY6d84)q1{O_=J_?wwPWMGd(%nBa<0udM7HqxkFwiB z>*7@1(a4b&*q`ef_=osyn9cC(k|wn@DdXKn$}>C#A6o|EhDpL7(j7^HP=IDL?=}sG^Cv_4-;_z#W810@-d%W*qT$;1 zJHtc^vja1n^ZByaKDwu(biImzdWGHuHWr@Pc=8QY(y>^r{+Ls`RHx1l(0O1nq@ms(Z~Sv!N)Nw5 zOmI8bLz(|tKOo3o`xG|YIjC@O(>zkeWaSQ)D~}+-=juI`ThlTqXOT)*9r4X4D+Fw}^~g8F*mydy9K@9TgYTHR_?NprjivKxegrinPt7KMD)C}udw{Fk5#17kCS#PMl6PV)D; zK$-Py=<~9SYAG*2fbagYLIe8ic{Qv?Z<|Ml>2bUVkrwc>^FnNQwPE2?J3ya5X=C0L zRcJr9i3>#8OEXFWys47AGIm_g`{FIgW<=3-9a`It`)=b?{XvWadU%q~)uQq)fk(~T z`0c*!;M9l(NR=zOE?vWF)s9{eP9%r;`pw-hxFOD+^5qYKYr|ZC^3matdOk#oh%+yZ z0P_U32X}QjXE9<=-mJ}KSoTX>O3%YPxSvx+M@)=93Xe@USXs;ker*4~>++?*`}tu! z>*AhM1-{`Ud;RV}$PN4COS6TU<&hl!~3cWbgWuzJ`n%hH<>COngz}t%*=nR*u=Hea`SWqG7MItZ%SfGOm ziPOQ8^)-H$BiBDXK?|vf1=_3o=UxlYHvsCx@sG-)!5Bs^hq*wY=eT`!W*1ozQd z4|-OCgDX~N?+=c@n5(n7GcVm{w%keAHb*x8bKh0Jsae*>-^azJ7{AZ39ph; z9yycx9a%A%#b<|7)_hgjMj0AA4Q7l*>8oZJCvbTv^Ur`TPy;dq>?B(O@Q0!i>;jtT^+terW{?VOsoQu=Lcy zk>iF>$pyC!SGCigm&@mmZXEMS>=@#vIZ=txV7g+u}5p?)eg2`2*cE@V^h+`PY};!RT@U%~FjR&2&}+`6bWq^l8r4cyR; zQ&4+Pfw?A^9<+LqQT!I^LpXR(WYrq-t>dm0@CmeDQe(TBNaeKP*;MO0~QS)BJiQ>cP2rbD6xX{JfUOlq2ZKg;G zgkYahfm6?-r2}~~y%_(O`+El2!Jz4Hjt%$<#V02(gO9X(C`%-36Gq?t=8vRK<9rG% zXN-uq&vQ$Bcie}uZ*>y3ClPH3Vzi)g9H<%I$F@AecNQ9y{et?d+BDL~XlJCO;}yyo z%LOF|KPsyLD7s;v=~p7M=tHWY)HpH^JkxWUyYuef4Ne1OYo|g>n#awGDHNT6?*vf3 zGS;$={_)u%OY~wnPQJY2H+3kwz;+a~jmHWbiC>1*s95Dud37bZ#ifgI5}&=V!)!^0 z8nzPSB~Wgsa|!U&xJvi~z+pUiZzz3RkS{D-7gJw0 z*S}?lr8kJbCr;j6IsNb>n@~v!tO`CpO zUHkaC(7fjr%SW6^;hY%*4-&jeq(tKpTL{`jeM4nAgo*gddqn-)j6Zi319blA=O~xi zZ!Psrn{O|-C@*J17?5z%c-24g-izic%GFJQF#cV#59aBwx=}C&QVNOwiPOohkLUiV=IZ68vh*?A}#UPWaT) zAMxEL1^dB>6C;J^gzIHa4{wB$$D87Os2uAlqDz{03+mDb?H(%aZt>^lD#LQftO3di zHBXG(UA!udgB{s?f~Bv#kp!&EH@#Yk7z<2 z{?04Thzz*P4$-0CHHJs}wVly-Zjhqn8(!DZNnvLXsarp}HC!Vv84L@@aMqTT43aRc zBz39pYy!%EmQLiPEc$xm)J@4lLCUu*#aern^E)8S;)k;_;|*>Qescw{sqT7!gjN=> zT{kIw;@Bm$f?-w%irgwVsy2snO73n7BSX2D!QV`-(w?L)I}EbP%Gj>D-KNx+d(s)X zhN&+FEeIC2Q&x~q6p36tiQR}ULFD9^n;GIF8^34M(tB`}U*YWnF1rUu8d{w>j3>)g zuVAjdZ%`5XM1zn4QW=G)yiB4SYoC_XS6V z{4C{svQF%6u=-;$L*_Y)^$HbMpTP;C!(H&g@_o8wxsOOyVHN`9)|<-m0APS)l>`Um zobJ%bjYvM~C(_n+-bN&8)O~i+$F=Z=`htPSZ=G)C@=|b^5;=pTRdZO8fv$E_Qc0nw z(aEwP{ix+Ti6Gr2$wV+~fWPmeUS?a|>`dD}@%mNA@4iMD2|?{wowB>O27H=;#QK$9}vUD4Z4zr+`$B@4|3REU}xOfM?$tRfCt=0crd6BdW!{+C_S72dFUW`RJonSzshG)1H3566|}qBO9e$t7m>W$R1Qh-5_f)j;mC??(oa`go`{oK~ZZmKRhfCQ2q zw_3Rm8LfLcq<=Q}M8li-Gc0QLHJdZ>z%L_UU4XFyHwn$Iu@6;-Bf&}J_aJq+@Zz=7 zY#1VoGS0SDPixrcS%&IK-Cd2vwjY6K562!(4E&xIF(cC$HiSMmvuF)lAyqOWMUoiT zZ$CZt8B?`J_Zmtz#q?1smG}YE3&Yf2cW-Pm9{iqmc|=?J>#E3NR zXSloA*H4rtTZv|e8|-7%x0%iit^Dy%GN-PthRB3F$7>>++eKrezj+D)(nxyy4F z@(|=?ZMnG;bn|=S0zIs_D7r^LAIN|x=?Pc!PIo zKamRR;@4i&-fdLSypPNWwNe1K+LCkRTs}xY=YRD5D1N)3c); zZ#r{%&1rrD?X+|n0e>!9b>?&CD6OW|!gBM29m+ZAm<&U4jaMui-ccT$c5IFU`k!7S z%}qSUCvuGTThJhL`I7p51aF~W?mY${Ww&gXE<_13;s@(e{9l`yIfA*b6A)W6v3l_< zA0Ub6U-P831;O9JNq#KK_)JA)4R3o?38zq1a2QmLLs|_sH~p>c%k~=2)CXd|qAP_R zcOGC@oxZ(uSxj$1upskDdr1gB^{XiIrpx^Y{EY7OR{klRR0&{BsKl46rsZMpV}`iK z>=YTDf>l>&I6yYt4`lR-FZ}Wk8BUwvngvxnHx(^2cHdq95Ih!8b?Z*l*pU2y4+>|I zzfL^eyG==>iJ zJDZyIG#%Gnzzk5&oQFYuvVyYHP*o0CIcYFDHU;Dd=YLlB(&&pP_J@BBw8P3ZjAnx| zTK?8Gu%kcUmK6S=&^S27F<4>1_ddJmjq#1xgg2vKtD7W2)4bT+pVFn$@7L_KV4D#4 z*L#Bron)$d^|KcB-H|g=+0x)i^1S-a?}1?*O!HP)QZ{PNV*BGua_*uB2s*Mf`mEud zA4fXv0ChZFEil5uq_`>ZM}q^JAN3Pan9%~;$Yl|s_7e5+4L`8P>GhzS1`JnOgJD*6 zKqgdgLe2vK6xE;wd)Y>iR$k`23C6^!S2SnJvO$@_D$2 z!z;?0aTakFOd**sbq{TbtuKa`OdxY++gIFzTyAD-tGCz;)K^#>o~w;xGG8)f1n1$% ZpynE-hJxLKm%pO`NHty63T4aC{{b+krG5Ya literal 24468 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 diff --git a/pkg/ctr/assets/fbalpha2012.png b/pkg/ctr/assets/fbalpha2012.png index c0cf19e75d601e2a20a60371cfbc65a284230a11..307f97b3456d0ee8cfa70e55b2b8c232c3571fd1 100644 GIT binary patch delta 420 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 literal 2320 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 z zVi2oe4-h(b@XXNosF60KP#>LjAy0JTra4@f`*F&*xubCv6oGG8kGTbr^%7OSoD^Yp zAvY+=tW<)@5*aH=Jn?k*_#lW4>s|DBwSF~=6u(sErC;S-H)D_S=iF&1wi23P5sypQ zZsla-5I4Hl-0Kvh!=r6#5ZnK+D zP#4)YUuK0QNI6J_n&a3K=rYf->^#P}b+9JjMFlp6w~9>6p@K$j59cCQxI=ir@=lwH1_jbmDiI z!_vM`#EimSdIfDt5*=IA6y;F;NyKzv$Qn~Ew!I67I3Yz=z7a|U5Mv@;LKiAX9rl{1 zzK|(~uKIFUJCvc)=Y;)}!5F#+aNWTDfR&e@$tiVW_Tm`cq1>}c8npscSgHey2d}m9 zV-^h^CwA=Ehq?BeXVFZrw3kgt7;xk1jCcD~6rfxm+o5vLLO&(}mt$67MKBLu9nZOD zc4z%4IkqcT*(Wsb^aOqcKT_DsOTPkW*=U8E`%VK;3Y-w`kmJa^o{Eh{frSJD#fKs1 zH2id>@hG+(Uv*Ser#AK`EfnUfw)3cSR?T+&?i3aWM=ITG*<_3I?+*~_D%#4Giq>KO E2O^k>82|tP literal 31783 zcmXtA1yEGq_kX)Au=LU;u!Pbfs7No3h;%ndcXuoyp`;*)fPj+H4H8SI2-1p#gp{!K z();H-zxm&p_vYoDGv}Rq&OM*=xi4N*U73WCfe-)yl4mLk+5iB$I|PtWyu1B+WAlGE zAl`D%bfI@G1Zop^cTV7;V&r}I{M>&VNRBHz;O-*5kD{TEj=P0lMK6_u10RZ5@GX+`QfSi9Ffh{&hdYqRg&UxO}E#&^++r{+l^lVA&sHt%8 znl>&K8=G)EA+$8_=H!C;g|qX*uSb>RC6%kYXHtwNj*1X^$cF60IHcx%P27G>;>QO1 zwsso9r`X~D1i4IiRa)eN6Q*MMKX za_KN}Zq|yOv!3*c7pLAU#r@m`r10;rLtnG!>Sr8XzxbIR&P_o-EK?1N1OFg%GnXbo z{Q7}Wg&X>!XOiHa$7`CJCX{^zmK+p5yx?!3+}HU=)D5(_lzQD-0400mJ0ml96ZsmU zf;U~m`L$ndTlZI-gAw$G9YA&!sJVeZQGUJHJ;HK7xnPByUGVaKdL@o=%{d^-A5^!@wpn6Y}Lqj)_) z>w%$2za&wYpYM%RJ{~I(#7fyxr$-xJ3W+RaF9kcyMf_rz03+W|xDqPqBdLKgPfIKE zc|Ga|;E_F-D43H5y1)rpsOHECF!aDW_T2JEk}WECJ-)b2yPkFT90#R$V8;Mvzol^N z2(JzI?-QEi7LF9g>K7ezWIA#ss;#T|dOL?Fvg_^PO3<960XJik4#HfUXeTUn8907X z_wvQX((@s(&ucW(2?MkR?Fwl9lSE~+(97 z<^IZ>`tO=9BgLjjA3U`;AY=|Jpy@eqi30^dRlZ(}Ko`I2^l&uD{r`&0+LaCcK$b~@VWF-x0 z$z=G9zW@y1!4*`w(nLtEM7oX?OKNd1yMac(UMx5y2+ihPln=rUL` z#8h;`cOraziEY0+kxc8hrtq5dXaQ-}HobP((OlgZ&B3ONfB|n|F8L^0u}3fyur{0o zN{&ncy*VYc*cO4()qE><8KF!Kdxgf&1xY0D^%Z}I0da<2Ynsod;c*6jG(HPX@CtI> zm$FU}*?zVya4Fa^ww4My;6&q4PQF#uO}=(1AtHhZCZES$ad=bnFCgw7uIV#GJkfkO z%b#24LLq=<=jAQ+lc(t9&KA^jmFLkG;)n~^SnnYg8;Enu;9A<%H@Gf^90e?42K^TN zPcqDjfEvSG9MtlUSYeAw+FSJ_@mDXLgq}M&FI2G|dYzSO!%zEwNZpl1^_6rP8`<|e zn{l}GL@hXdq_~Aj@LGllc)dzn&gY1HrwG!TX**rfF;#cYKC+y02WB-x)The-F_5|L!OZkVJo{@+z&t^Cq9Ep>w;gx<3%9x zK*1WsS{5eu5(e#=W~%@fD}8V*#i@KRG^yYQnH`Au(a%QpRIH7aP!ne&4|8XBwCI~P z>8(7u<7=Z_%Z1;cubV~#CEZCj(Zq7E2=)@Jw88k%2?Vvrx5QvETn$-huGgDjvWSpQ zfhgapC$>7B25-fo`)Fx7D_Fu+j(Gpv_T|sw%a65)$+xURU}wT4;31(dOz3Nw;-p*tGFww{w&GWD*fQyH#0v+^|nB>WZb=)>PsBF{wq2R0h z1UyL!IOb@%gB;cbqbb36f@(j9H;FL-c#NPbiisaQ3vMhdFhmVe71Dvl;|M{33$)1h zBEg`5b26%>a2BKyT=4v`&*GPdXLtp7XCvZIICZpMZ|MNBGaVIWpAer&oj?>0*hpyz zQcK*sv!_E9laJNV_@WHC?b|7-JIWMPebBG_Ya08a6>9zb(p@EJU&2HVq))h|@tscd z*`LrStd>Nh;3R^+SFDIHzdh0Po0nYo5Z4hS zq);v6>%<773mw>dD95D~Wu)w%L)T{)9nI+4p>{{`lZ`e`7b3_?Y6NQpHPWF2!|8df zU|Hz}N)-VvKmCRQh`(l*PULtWyb_+P0JWb$>b@RKoY5S(^RK|g&xC3%@r-NcdV&PH zQX_EiaJm!EOt`JFSv)D}aj0)a*L=`Dq?UJ~MM>rB@NC5bn}Gl9U+8ei)hdn@3z>U3iFSbM_Rw$Z4T35e{4_PF$K%@FW7+%%cWNdfWg{ z8%ryI+1#j_s|~n!rYJLouvO$e6dt@>Na8*5w6TDZS7_nx8{9s60(~D54f5iG+rmUD z_@4s2m&3x{aY5l3J;%KrX+QkKS)Wjc>U3sX+a8=C8~z%oAk{J{H(&QgESGrxbx=kn5jIxXl#XrxQ0+Ar^UBk zXc?RwLpGaK(%sV~R1)}daAI<)p%*p^OHY@vh3Q_r^6h+Z1BH{|gm77A<<@0VhC%2? zs7<5c6_P-7P`k7%L&S><=J=-y@O?ZRsIYz^f}RRDZs2g15CSF#rH&;cl|f~w&0#jn zj?5`41dyWW22oQ)WED@{$BF!a6rsgyA^a^&NEcyQBoA$Rvl_-JO^oCOg)X9KU)*eO zzVrH*+;iohi>VghawlPj64bJv6YE~+lw5NYgG>JP5WkhX7&QeDmUf1l7q6aCn?VNx zL;YT(wYtTrG}{=%Iyca@R!h9A-R&3t@z{TN?n0BRK(V6)or0VIOZW>BXM`h>iMTL5!>lT!0D&<%96;GZMjP#bk*fkh=RD znc&M38W>nxi^{S9C#jE6cr=g;R)kA&z}00Bk)(yS+&EXk)T{nlA-eAz+_DR8nG6s( zTrGIH-7=3Nl;gB5B#)3tXtMPf?u{bqA5TQgzJUyBh3cz)B!xg%sbu)sL2FdMd8IRn zBevR2|8&T9JrJ$ord57lNC%{Yvb=@Yp8^kLGXgm@qj6?;{Ha0Z zlS$f}^Ld$l3NY-e5$J}$VJJ2ymaHqk4^Bd&k|2CD;jib1Lyniq2KYZXEfivf$?;S; zN@=-ke1a6H5Zty+z}*@3<$!ia;K>FXR5l2PM3v8{hJ!&8MYJZ-IRB_D;6gPjuVe=z z`EjhRhIz{PX;z743D>z0s^N~N!g#QGL~G16^r;SqrV@ykpj#o*DRUx+oR5#<6FKM% zbfp0%Le#jekgf1wzYICwX7guaGziQb{*A|wGw9hbSiKjI_$q*6c^LUVHD=@-pFRq# zF2`AnD#E*p(UZFuTGeNN>nA3f-SOxo8xvKG;bxQYyz3G69* zMRCGT+&WjjHs|~INXPCGLRO?@!A4s!fjZw)(k@!;w`PK}DC}v{zRG=pn9E;01vFg8 z%8l&&X$n&*b&l;VZwVdcByz0n*5< zBZly`@A_oFMWx)c>$qtZ+vSJrdY~=&qN0#lR<>fPKe^`+QGJ^ry3L9`a}=hOGpcl1>B&HW=M6u<;o5L&fotW%55< zrSA@dEYG8J$y3hjESep1TJ_DjX7=!-yC{T9`egIjuXLcKS$K}V|zFChQ9fH=Y;S{|oc&{GIn zQYM}Os^w#uGl4fpP3xpel$-<4*HnG|_IdzH z2hWC>iF%k9Ecg_-Ir_$`T1&p`PMZt4dr#5FCC)07<=Q~I%LD|dW z8~g13`oGr9M5)U(jh|iz?f$PDmgYi#Jc6fo8H=tA(t{!?!q~!yk*_v-pg@+l=9gz8kxY#>EofHxo>ANdeQ09dOO z(}Q^NVBe>r_2>0KI|$a@cIjFM5c!Q;mT8y9#p6RxPbm25;rXg9Cuwf0T_HJN?|wTf z{I-mps4YqANjMYx3#&b;^5>q%^Dn*UJT3VuZVvKY!)}H~mKMnPq|x`>KNomiMJP_Q z&x^_gUv)(N#U{6DnB9D={ut2z>XsC+(eFGV68$xZH`cb+HTj}^(Zy#%@_|jw*>P{v z)lBbb=5maIKMI+{Ki@7|M$qZ9uS+p{k+m)Ae$j8i#(pPsU?yhojCpnO)h zIK$kq7EgF`+**X*1p^L?fe**~&lQHq0aXX&JUHow6GLJ1Mbui0m|!G-4Y$4J6PvZB zQ%W5XrzC}C%rpK9TaE}tm&Q~Fi0?NNL@&~i5uNn3TyGU*$O5S&W5Blk!a8MoouSfp zQkI|Ujk{m-!snySa5V53N0hH{zriM{@{_OXnJI*jDl5VuQ1of=?axqqQUnFE^qiE? z$`z^!T?I1aQXt}Ls^Czq_o%oiS;3=#h&cSQW$%sI&sQHCZg_*MetSXtK=e<7w&P=5 z)85Uua~;k3=LH?+FW2;SFQ9y`ZE1J?1BMQxo^tha+9|h*-76EEVZ83wi@#FSh1F9`;u%y@^OW*E~Xt{+RemBnYr zeSRdEY>`JTydEeY9XTmLJWCGSwaJgOd68u)A_^N4nw;*61JQ4QTYQjQ^x#xrfIdEw zg-eg}D-5ApfYc!STL1t;Wv5JCAmVC>4FG2oAZhr%F|fHGaPr?y7xgnG2s(8-x{J5{ z={|F&#PF6ysH-1#e5qz5_2^{z=3i||rQhU0|0R<(vE*%?kjT<;C+3rR_1a8z2=(N& z#OR;hhog!TwBF^1)5`nVZT~jr)R;j?EG92`k)uN@P(Y>AH)P|3pPh`k2%~ON%^Gl0 z#r+fj5w@UHBS>%YQ*jFeFh)QeZrdaq#oU9u-%XlHS0utvuroDl+SEe>-d-ElEZ!U< zzS8m_`Zk}y$9tj0e}Ny_L19Hh*y@%YRAcmnPru9_UIYoNp|=cz0aMSu)+fy2!I5uU zm^4Gn)xbGAq|yt@h$P}li|F~lKs5^ICI*d20^|r)1-rrxLnmlTEKZl0Eog|^)S;ES zS|ob8^XmNmASu7wLQ{F*+0xATwJ27#63@YM zmJMTqy`1^YF*?;oqGm7t;Xy5q4+a>>n!gRcGms72&Y7)*}g3&mIF3g$3 z;~>=oE;;stC>@Fb3Rl`EIy0PP&FzD~bi^m_;*Xa$FCbAyLEx!+p%W6N#`4>WkCmvw zo@IgUmKNP_Wy#b!mj?2vC9!)5U8RhfFCU5#3dgVVQfPyp5)*h z+|PW(hj+%D@c1vM{}efS(;l-_iz{YJ7BDo4A5>>{e!+v^)%#MrxZrT(MP`zKo2$~B zDE+IoaA_~1xTuT9id!JhrJo4sF(Yv{fCOJj zgyOgkC#%ozEri!L=2Q6uh**fABTnPq=G0%d znt|J7fkZa(b-zQP#9esehCh&6(}d?vaxXZw-MLm`h# zp0ja-Ar(1d6A!>8q5bfM=rucV-CZ4iOUnwbHG(?J58w$+skZzW>NAkBy)<#h_8ewk zmB@5XEM9IzJr^O%-B0+%I_R}AdwD4JQdW5D>GubBImAl?)XjIw<{Wg8zUA4?{-Qg> zBXr~3c*rYJVZi?9cqTgU`m7BleN}P0O_t}h*BS2T`WCOxx7l3Gcez?JctQSVK>*$7 zfH=Pr-N$Z?xnut_PIjgsA>A=odf&k?+Q%Ts9!%B6`w`s5tTqK{b}eZAcncJ;?p8uCO>5Wi=;gJ+ylNlg!JyFHgU?r>`& zCjPU-$!E}kQzm@mQ?~b{tYxJe4>KAVhC}V-Vb8yM@7o^b>TM<0t&*J!xWH$T^QG2l zpm-Kq&1i5ZLPZdHjIZrOa+JCFBSc=HbPec=YGVs@0Lnswkj1|ufAKJ zxnQh&T|8XM>Zt&)m>pIrj@sgw$+%i<=^}l?Ll_kz?s4z(8}corOWN{}&BI&NyRFY( zmE_IJG91OM2m!q=o1Kd`eJgy`1D2k^B^KR6u3kIF#{R^B}k1Zv%Os|24)WIsR^5*STE@x2H_H9;b+9`7AHDvQG?RBfYOSn%i$Yajq z+&FX`JS12gz@~Y`jI=ZW6u>k@tV}of1}E(6Xd}$g$7N6a)$}}JoSK=BYhf+z24TuQ zocaafIl^j|sl}>bzneHC2O6>o{o6Dk1K?{W2!*K&^Gja$jWiYK5y^V6Vcbf_~^kBGQRzQ-4ap~n=_1SS?=v<-q&k^=Dj!K zO+Q#@>)#f9+zTH_6g}hfzvv8YJ;LcC`?s9DTaD_p&*GH27(QQF#)g`6uDr$H3>AFX~LwEweXs_}jbMAD}1raDwd7Teo#?ELg(#>8Ix zxAz7EO?Y^ZE3XAj$32ADeNurQ>pbk-*~N#)yW!-#V7mu>O$v}pgM5u&RbVmu*qFBw z$QaB_RhapfW=b;f8?@pHq@^Jy!4r_LybPBQ1hC_UQDG}XG9D4YeCgwQuBSlWY}ELv z_ck2=6@+5FP`T9#`gx0LeGr@y)rXt*noVS77)het?`CwWiz5e&>_VU`$U;;62~<0c zpUXXr7l2Wz=e+qL8rIHK!Wh74>BQj_dY(A<`}=C4$FdA;$KN?T@20@H67N!l+V2ijtn)Pj8o0+io)#`eZG| z#H$C7C8-P&BN7@~-JD(f2SrxM1B~2`wR?FXBN_Oyg80*NX<_G^XW^aZtVg>Cib{am zrz=zK>E_$m=9N%%sQb9rU@}uL{u1W)`ij%-ijFy`)PJkXmF0#jY-0@j0IOc{YE?eG z-REQYzh_g)#Ac+3&(^ES*Vnh>%2uAam(RMC)%_nVKK?av{3aOX@8@d0C}4FSRJ4oM znpg6+%VMJi?&upS zISqNa57e7wyK!C8Ke7XU`rhW^9jW=ghqb8InB52H5ez(FIOe6LT+VisjLw4^s-j*5vegc+A=2(b%_dp@JF&JbgK!?(J?-R8XIo^WJ{>=w%Ri4qQKZ_CqA zEXs%Hdj+SSj^e{02_(;Ta8QESKeJ}|Zf%&8&WpwdaffdI_RH6T`z|q#7yg&SU+b<< zcdKuD9*ZzaeusYW`;ub}V{mv2^w%<>6Bi(PpX|DGCxG;Xd0+(;1eAL*?(F7Fn4O{XFe5s!J9XxM_-nI9-vh z#h6YO9x{y>33mAF&U&@nI!}5Kj$GeRJOXiNd#n}bYRGV*AwL9Bc*e>5PvRSjsh+BI z@kl=JwF5I~iWo(&30oXvCy`%Y7EhJ0`EpNs1sA4I>gF-HFXCm#8=ri0zcW2LuAQ#u z{ZzNc7fq`3NC+P*l7tw^OmuJcU;%CMkp$AM^iA{~F#tLr7i|5tE-;5)<}1nsVz0kDH_x){UAPwb&Yg_0NFOv{+W&s3-u{b^ z9}Zdhana(p5bB*-nQhNKKM$niW7FqNU&O22RtXYO;F+#zNuSKM6$>9%Z@ePB zqBp)~AMhQGT$@uUdFc4yTO^DS6ay~{t}sU+ao?s`QYaqssh13<$}EJmK94v9=1KF- zY=931a(Fm$9aZ-be|eJBAc>EEC$mjy1yS)(v3i+Bnm*~W+qubN5{D^*(&q>(SUBMa ziSlpWS{~g4?m&Z)N=I&1LppFlgny)kvETzMp>{t!xlIh0N_vitmjR$p%isJEq|jj?veZs=i-p1Jo;##D7{*yy(G@yFXi zjHJ62RMjfxW)r(MY;mt?5qnM+8$)e{R}Za=q{O8mr~kcDTRYl>;JpZy$dD+4=|b?>zb&5A8}G z3G88t-Q~UH#stkWoDIJ&X-Z@kg__BBg$%cE3n_vgMD6e+!k#g)Nq92D)HuIadm5y| zZO+X?*HxqcuKl_WX997nuGqf*GK9NdXQKs`^K59X5hQrDG|!6cocjukD5M3Q1QCTE z91oa5=k`YP+K-N|UR)F(Wz$Ynhy7bg3&Pk3q>}%@8w_L>gwekgdi;3ictjc3t!BT* zsl#fwPoAbt%q<}T3sp!X^UD09BCWg=Y3 z8Pi4QynVS{pw@Bj(Irb)t7yc2;R1T?7?=s)yogUY!gmWL8W8~pZa$Jr!V9X}>>4kM z*R5hQtTufW0S{3B1e-mN#ie3q0 z*P``Hh^Qv4=P~No|^Z3;nieyP5tUqpm$Pzvz;iJO7ewf+#wTa(Ag!d3z5}9W7N;}GT%FAxLLr0KeYm+!`M875dSxThQ z;=z4fFr5&J3lbRvY&=*FO66U(~ zzw_1#1AFvmIEA=j5>exLaQ&UDC2x(kn5S@op5+juT%`%+zgjYpem@^u@3jqA(I9>g zEqn{cYMzR`2423g4+6U4%1;=*mrH!r5a?l}H~7Vb09OUH!V7q|vl>2EsB^raI4Yo2 z8x^=%Hx4`Z=aB>FF;CAgk-xUgyw6ohq)2HC(NwtUhjIz#j5GSo5CHoZlYbU5A=?LT z09|gWuQ}$qXh^V+%zdG?;{?~zWCV&deke)xWxJv_*~JIOd3 zt3D24KZIp!f3>mueN7b;(R;fTbw;H|sS3tR{e*nPem=zQy(9yn!aY}F3X5~Is90U2 zeQ;+j-P-Cz;_Q}JFhrG)fWpBc#XY07kB@AMR>Up@#2Mm22f`Sd@EWy|g1F(?j zQaeVME{hL)!z#M>SoGg}CVG;}{RJbHF4k&$G?Q8Ine9s)<+7VUn@Of0jj29U3f93+ z?^&P>B(jNR1{v>S>TK|#-rDHgo`V31@pdIP;G7Wj&1P&U*gSZqBk{}6mc%)+A)>1H z@2`4U7K0snUv6WLRKLAD=RL%{B=4nNTBh#q?!VL~3@=*&ARV64nopneJnkJ614Q|j zzS`T^6t4gwdT_i+reC`_fWElGQMbXIN6W~2Q|m%%B^u#xuVve;Ca9v+7vQhKHde?A zCip7s2^9HAL5Vz04#tL*6Jm?gwIq)q2gT(xjj$njfLBNp8YqYWM2Prgo6!@&04BT# zhI*;2Prc#TudnY|pNvFA;0LvRA!#RX%bhNK*0jVf@>!aq7)asBp#5HL_@HM#KXCk)L=z$TZ{L86 zPrl%zI`^+jfg?$&5hUW$p4jUEi7`9|X1fdnbBRVF2WB8-)294vqO39EB|7|i*aVM3 zLa=0RawQ3e2&Z<-Tad+KX{Ra~Xu0W{-1_s%4YyZg4-qf-GB0?0ex$0FZK|#yy*d|j z9>&=~IH8A4eS)375QnLxv{oSS_D-`wDy(lK4e@vCIfl4!f70bcBI!~f3T7~Cs5Q(N z^vv>k-c6fqf_et{fvf{%VGi2{RZd|kT;4@{I;ESfcO+)0QoGXh})7hRE2x>VccihzLwqUBK z`Jb^vRtAqL`VGTn%)8#MQ%8we?Z?aT(1oM7ysQ@~D$3i)2T`e`UG!Yao2edjpjw<_ zz2*{M2Yzc#ex=)R}S45qGCs zO^BKZ+TirJreogOee}Hli6p}TH8P05bbZfp5qn2xCL-r~2Y$nNzSh}-3(#QaNoN!z zSzr(dKgO#ja&!(~q*FgxD8K$Bh}hrBmWnM( z3ucJ;tA~6=CprZ8SX8c48zsw1;g!C)M!|;O-8cR$@s^jTp_Vhyt5=+N^9%l;_At`J zev^K=?O}RqtibBeylFV_@$cX1*rGxOVa*7D>XO%upE1TOSEew5 z!E`3i+fHm)S@A|~34dz))!}EiM4ozd_;Au<^TA~iX%M^Pr;_gN0agT4m#Pxo==tQfNt@qBy@M#Rl~1}Q*$ zk$~%Xrd_1am)6cV5Z=hbw6|=WyIdfts{uNQ72sU?QQihhTrjoYf+vvh&Cb%U=AqC7 zWFs*pUHZZm?to>mNCHOPV0F`M(KK+WQ^~nO^=Hk8;};henuUF!l#>`>UGghWIQQJ< z*Yi?xOMCe1x6Aif(_T_YUXk&9e6`)ezdP|b^Z5Y@Xx6c|6oc6gto=}bfLrMKB{ve3 z&$LOu)Qy)9$|u1Ox++!1Ag(+}N z|BN6J4tkL$Fk8oQ{yo+0rfTrl8p>ULf#ovd@1)?Oq?D*g&+vut#3rvkWLd0MzvFnq z>!!BVZ)3~rUenjvLThfDrq6mW1+z-~4Ac!I#@nnv`3?tc zc`pl@>naTrT%&FLljq((FX8d*S`Mv0KQNWJ+#JkM+86t3Ec0kNDMS2;gRe*uIAfbU zO)Xhr+gE~dkL9<-udiVSpF_K^rgduhA>6S*=T)_M&t0iJY<-H1KdIVJgqek4HLuNT zA%3)^yt17~p9p-fU9da+#j%)znRbY?6slTi2MuZv`P60*Qh`Tm>LM4tVPiC8YD%7$i;X`*-n|`FPz(AuR%rjai1X zJ-WdY2c*(i7qMyqB}fZbn-Mql$FVapOwWo(4?dbw6z``-2=1V9{NylVtl5lfLl83l zn909z;1!^v8>Oq19!urv$`{gW^?Cj^iHkk%q_NUlLgQRw-pX{coPD+Ha_2;VqJkGf?LVM`7P@oK2<~a$xBMw$v@Pvx@4eSABh-5IuVd_#Nl^eHebHsTX6B!l z)4qAG8oILYE0W`QzgvE5Pb-3u8)kWk09YR47HwBh>(p(qSUueTFykv;hBiwr4th`ZXD_zcj{A zgIwaJk5@)cR87guvFRhGdS83<_`I3Dti?!|&Mw%eSi&vCra$)MkiG3C%H2AUvp>SY zNIy<(8q%JLWTp#}RU4c|^3C zBSIZ@s;2o{v~$U}FnoLj&yoT$o`C88Af!xG4m zKdOg#vJ_MiRh#~3cp94E9;Ltz`=OZ-35)w8K$KSE0yipt75a@qwIT+#ph#oCjhqdWL4#eH zo6cClz{RO1zUu)Zzj4LC|3q(%y*yQ2Kr2g>mA}TrR<6TP7V3PyQF)iy`2H*=uVb}M z6Nl!5`uvKrJ%fjCO^7actH;y@e6^32W&D2r3*1xS-tm_MJUj{*um5tAxn1q7@yfqx zQ0Yrs{xZ?&BfUAeLiEdYCxys5xin|d3x8F`7q7s5R-B@SFcx<249#&Vkp_HaOhroT ztGE=bSV3&!>xGNFT}I1fM8~vPR84E#f95uOvk${q6+0+CnLjLzCf2Ep8fZpFLTkX* zczQfFS`d0?rwrRb5?y=CFq$}Fcq3xy*LQvn_)tR(S1B-ntdM?3z`nLpn-%H*5LBrB zo7~p`F)Up876eU6j81uqXs3DEF8zA=2g8Hrq(3H@k6Gs<+PzDU zq(#GL@#{!n3LQ{24)qn|u)&PXBNypsq`B-ns z!jDB|eJ2aCc2k+g4=1JuypfoYz3IMC&V_fF&i$RW0x=@TAO0F<&7XhVc8?C=NmsB_ zK7Zp}mgUa*cioFZQm9Qov3{v-5_|j7Ui^o}b)O#R_sgsMM*DWxF<lyV^Z3>q4f5 zMC=(27wWGw z`RBe5xht6zx7mxlhpdk*o0PAXbYp79=MFD%n%$VMh_($2JA?V4$Od#n@y`QN{7Nt7 zd~a%*A~&SJw`BbUMPI@iC9{Z zdQXI6TwY(qgGLSv=Km(=QX!*4BLot9g7f9^v#0&+|(NZ%Y7Ykp|{ZO%;uoPEeSQcu)S~k!CH;JEhmgpI?_6cS|MPrp+Za=?}K} zE_wbnk@0o7yusu!qw~TpbRUOJk?l(!vm0nM1OTazjmY?lQ>0z}KVN+)aWksbKe$lN z+zAi8L!@$JGlNIoqC9fjRdAYwmd=7QBoN8vHRFb=2v>@&w7?Ym(;iU$30^!0D*|G>5lr{Y>HT1+4F5^Nbg2eA7W3uxRwe=eRRG8z6ErIi@2de``)j6h!UW7Q{pBCSfL zxG&U@C-M`8sqLJ4zv7Fqn`fVO&=;5g0>b^)>&l!km4d&$uf;Nx8qH7luP~&B9S7I8J_(?I{#h># zWBC)r^~qo0XcG|F)Ge)XW`rEa$O_K`K04q`Xg6UD?xK^@1DRz(M%a3Wse>yyb# zJ|qO}PNsmkoY-8{9Y<=J*6KPaTijoKaILQ~e4a5*fE%*CqI#;)8Q}9pl3$o3{Cx7c zeYg)+m#8y(onWZ`EXhyi_V_CKcF8_j?PgDObD#oRyb~d166k|v7XotppEX+bm{9G+Qowu@5c26ri=rKVJ#^Y`+CMp;nTM>^!kfoP4PE^OG; zSy3L!bHVFN-+o{vcB6N6#sYiZopq~O$nf^2-`+Lvs^8u%kBrz|?Prj_#a(?v9y^J# z+RZo}RNRPS8PWeuut(B(*&@IqdD=CLZp&`5io)EI$g5vZ{C@A+rVT@Z~rc(LiD z1CHe$F@ig#qLkd~@kre)Ui&IOD8-0&^x@DWh2 zdUtc*@cd}4ys?_eXqfdde&gx+bQBT0pxEfcu(Q;D=B3M5cVl7y-Wvf2sE31!oOjr@ zQ-;xH5lW&av~^+h{_i?>myPBy2XvX)n${=Bp5vP7JKhS4NQ%|wi6&dcRAa=NFscM^ z0GdoeyuACm;PPaWf0KNGsNltvpuPDDD{h z{vtSg(~Q!ipr`MAk9O~4@V13?OIh~6nC~2y|E{YwOvIkGNLlsqr%00GC`C@4 zmG_yhuwRI;W5a_=%aF-@^2+>=6>#{Qk!j-6MnVy3(s;_0F=XSb_Izk=6HmAUGF8j+ z%AD!(Ob)nFAHJ8tkZjyIFJXrS=eJUqKK}jkR|VrZ2QofimIP);^Db`U$2W8gZ9rM> zVH@hn0_F1lz2vvcExwp6G-ix7Dhvxg+y0W3x}WBs(0~ebd5n?A+%v1ZSVhZDCW$!p zcl^6dI#fNnc`BOtx;BO&1}<}T;!5Nmw4Ze0)OF+0B#CEP1GCwx)UcO@N%f`{uwLZv z(NXZ@{L|TcaybYB?Phvu6BOci`Nayfnum}v!K+{UNmzuPq>_Hva{RJ_XShY+HN~xw zQS+kC)zENB|DB$$=e5lfu^{Wj?E1kpyP^&%wIn?uwl}3k1fS;F9L89OtYNm_oed&N z@q7#-1~`N|aJD!QRDiRq3f}2urybmb4F8KeF2l=T^$(w$&E!i|!KZUM!IQ)}9Hr3^ z0waxcyrZf%jil4f1?;J}-^VhBjBxtS0u2F=ypO)m>IZ=w$CAJ6+V@;oz}pLpr)7<9 z)+oW^T6+4cGlM+|zcDqbC258tE>Q^g8y0pU9~LPEj}f;5c4%eWc!wufb#+Ja5er}o zCNvGMv(nG!U>#L5+9r|*wo{fdBmZKRFKw?u8F_z9nGFjz2gA>XKdk;B;0O!2?@BbQ z&upx^^`We&)v|*8qs!+&m%vV0Ekk;H!;=Tt(idLnr6 zm2dYZ%)zx!AAM+gZiOp1T`{)y{2v%wz3H<0(XbksQ*otvY&B|N@pNTuVg&Wh zG1NjrB>=+FNtiRoK&B3g+nQD+Qkc&Km?|*7O$&)Km-_H*HTwod(3@bbt!N>t08s>v zsz7%E5^o&--+g4I+RrAxWdGFi_hJyu_e;jB>mW0hI)mGd0Xp(?vL0n~t3_Ae&mCAC z`I)EB4D1*9U6)=6E=4Hf+JOr^VqLF+zEzl4YL?=mAUVn8?{8b&W3T|Tu4_wU5y~04 z4En$lPDR^l4w29dvhGEp07@@{v$54-#5pCazFloaOz-+X&VRH*m8PVOfsVcmPzWbq+ z0w7L+)dgU69*82yrBBjQht(C>I0hNBp~w96g9|E8=GaO!zU zlC58Tyvo@|+r44e$l9kKjB}*)p0?s}Ebh zK)MBD$nSrVTxY}zgx&V6Hs3}DP~e_EY{flkut4C00;#}A!M&}aXD~oF5(M215I|%k zH#LI>P9!Pvrzxi>Jid_VBa~9t-?OaHw$wk3iqBj+cw6ELbXsYUzaVeAK(HnR0;Cv= z0_>gMf(kRo@$r#6?@LEM^{quaITo~^wQp?s*_SqsG@?44T+AA$=7MIoQmv2CDw@?u)LTSoze0MRQOqf;WHU z(d{4kxoZxOl$x=A&xCrEyBEq6tZ)6*fBqzg_O2fQkral*qXX(S0uad^B$I>sS)uS; za-3zjodBq^AZG2a-2qU}rBp*ftFRV@0T>Ad%K)lD@5T3UKT9>1@6T?tb)8ThL17ty zEggu3S~CaZ%aA^|d^w~lP6FqR72JH%r>**?3ql-sV+l=)iE519jWd|81ICPFHCe=& z)dtV6*^w`N`}D{szA`J@wlrGT?ueG3fA#3m1~>TlT(vQav1T$-uQfUeH#&Rp)g^#dfSp$$vxH8otpkmQc(#>J%8$-< zdP+!d*wtJcw*0>Yk+S!Cg7VO*wVsq-WJsoKykSoN#aB%%|H)K!`mr;eyejSBe7Ew3 z_dUGpoj-ZSk+E9vy)*)aUi*90P0gLvu(;hy5xQ!aTR6e|i9#jxD=rjfH%)AT78rue zg(G>SzyiYpWCH8GevCZ%9~wan?FrEL$kxFKU=_+dMn+H`;c8gpcVzmWvA~CRS>|F9 zM?`ol2MCPSW#Qlrv4#aiwm1R)q>}HVs&t_cx$nNSitnf*#Db84W+lXQZ3(*?jC$BY zDs?Q6jiS{)jD>U$&MZGpbE~%TrH3XOe|Pu8vUmIR>N78^FTd)#r9){P@Ugkc=IK+- z(L*fF5xYjUnn5^iftm{kIxSF33ye@7QKN9om9w)Oh%|dY?D$T!mX}bOJubVegFV~5 zC)cX|Lv5eDqrQd<^spCI*5AYzi~#OBywVf)T)(r?VqVV+lqE2X-*~|#Cw}2WkL)d< zvu_<=t-tbJ5A1#0OD{e8)LrM_@8E*PD>Kj=$BSzWvTkwj{l(_%GOP|haq+#+2Ed_Y zu<8m(!)<}imrMYoj#rG+8D~embIzS~7YIVhGg6g~^pJO*%6Se3^5XjeAVukb97K2V z3zxil-YU@_oLGRZvxcss2JR*7(1Aw)zJHoKvE)h$3GAM!Pz`{QD8Ts09yF^NL=sGf zH=>(82dm4U#oCBqIjv&Jb}^Su)9JOSdHaE^@rozQVj$`NZ13HJB)hNszR&M>AAP%f zdS1J;Z#)Qqix)u>Bt-(0M2I%SmmZ8PQX!R!DY+~qm2zY!R+X)kt3b3W@oymyYKz|PX0K*U-#{vSzxi+ z1;}L1)Kquh?&;~i-RGR|`JR`%jx%f5*)!VgV)eZ7%riT`_h;kjmmdT`8eG(09$W5h z+t%>XrDlIe&++{t@l(7^x$k=$&8N;yqMmzhoV#!-&-un1=Qe-wuKmlu{gu=E>&Ks7 z?Kj@@ZyvefJ$Ejh`|s~LvNBhsS7a8$MWs&e)e*oL7Wfbjc-hf_8qe5}hU?KuhONS5 zZb5Y<8V(7HZ&DeB1ir=vBt}E$!~_Q&KC#Nm_FjTXQ3KE^gmN&Zpc|=BRmvM=iQpG zjM0$4k!M)jz!=T(TSPg5(f7cRrRkWgHI^)|wSr5zDeRzw*Xwme=~ue)OT0rTTFvnBRMF zW$&jRIY0kj-nsAWFTU>Jx!GoNl?=gD7N8angqy0=BGbwras(18$l`ZA+%{GbFVPVO zoruYAV`8>UFCAd~7->h{i!Y}vA=@Dvkf(wdbztpW*ie|DU&dv0KtL-C&$0>ZuogbG zLK!jvz-Y!f4s5DcN@I|5>|%^phTnQ^&POou&01~1kC)?2X)plwiNBcfO|7H0HVhHA zDaLG&_Wz7DKSkF1d9sbqfSti*&*01!rOD_lwwTM?%#{gwqXL|N?DSUtsr!dZ&E}BV zc21|5p~z>+noYzuvDG>74#AH)H8r}im^4R{1u!XzH8?L+g8^A}(!KgfW5;yWna#n3 z0WiR9!&c)j)c1kNlwQi^hXDWl3n%wIaAI?!2JNR`w)gCz`JES{?j#2ICvUs{nHv|I z{onZfiNk|xvPkH{mP`NDmrot|H~-J_(mP+VboQ6-KDe@PCi^R|4V#n+#jY6ui6O5a zPX(UN1V|tWqjMXL4-dzKlN`+!j3_!d%gCpV2z4_i2xA3d;)F8MaC=(vq*e^H!FWUH zP8`4mG+U6T>b0w2P-AsrVujKQnG;+JK3F5~Lfr4igapYry(d!$zy#wUHEXCaYHR=` ztgakS7akh`Z>Xv&aEcYyCl>=quFk$fgZLn{Y@Q68tf3pK&u1UEXJfHS&sOUsw(MjB+M84 zEoN;;Pqa3e%zZa9n%#7rRoC9j^hXZ_7Dp4#`DUpjfPerzVU?(f}m(a&p1zkCcD>{TDANH{wI?Kkx=lkW?>iDtIT@L=Xo)4ZK_s1hMZaEXu%Wy1UWKmytt!wC{*ZU<$L z9^ZNh2mtYT>#;r{f!YeC2>oLbuz@f;!GkIYc)*>JH$s3+nnn{P3i+5hSx4HGnpTF(VKYrx=+&}-q$sZBotXBu^wmoqY5lwBOQN=?S{}Fl5%4Dovt%k z#<#^NcQ8c5fW)aQPF#Q^UZKAYalbLXmfBEgBoZu}A1l1X5Fj7{Jwav^8`jH)BN1gq zQ-NNrubiSO?~!T~TthKcb~7e@8DfT5`5dMN6m1<8;yqa-C$S}f!y2FEIde0xe`$t8 z3k~)b(DFTOE5$IQFT`3*dja2GfTE>SiAC^(KG+o3?@?8b*;Y;}YyM&gVBjVVKr=D^ za=hVHl_?JvIDj2rV{ho}YOhRBT`03JKYgL|)u%V+?*H!Qyq}t5XMn$d&ynMAxn<#5 z7lU4^{P=B)=ihqE{P~Z4{p_9(e(}^b&s-R8i&?iSNq+bKdOh)?ijgG>k)lPD6MST@6;Gf=`92tAA7_LROAphL3Ec8Iyhz zhzju$`uniGBgBt2m=i4>;_=2~V+erLubXkI{9*f0Bt-1U4qGfTtcIgEWs`>1>l;tv z?nh(`lIt+}5Q+t8&1gkcl491*K$Ss)uZER~R20CB^jR!Di_M&7ryxPVSWF{_*#kPB zJa1syZTwbW%Ya)mu+>BICQgLJ_369eMM<-y4%L9Z^Y;4<0Zeu#`2z5?pt}+%a_i6{A(+FKJ?Y)BTudNw;_U!ewF@{ z&wlU5&pf)e_*?IM>0=9*LljPU{g}sRoE@-%2KPG^0wI9I;PChwB6e7mV2YWsIGEB9 z9byQVt)d8P##sRg1_3K1mEa5{RnXlb*r_1^o9L|15!W}(0km>~p7I2dNX!$VLQ`Xf z6{ON)pTr5|Awys^_Az)W8RsuC9-PM{!NM5{v$<=q-_(j4tqF`H{pF2h)La0Gkeq4yKX8%yqDC;QV)-=!qxE3$kXDJQIp6p)j5_ zOQF?4ItMUC0Yy`%5Oupqn!-?EIMmKTE0h<`&={;!CSH)8J!Y0nDtm9>l8Zg|#gZM; zSeGRRcFWo^LtvBnfE4Z7(rA@bS28gHRCxjM#n`s>Nz(IO;)kjL~7D znS!YBgpgXMLID#(a(xW}sh2{Jaq&+h6`Dra421%nBGduQHiebGf%DL82whNfVKf40 z4F#781Fui)ZAeK}W8#lMP#8Qm@pu^=%A0;J%j~{Fj&JZUx1tn5|<^j*doKEDTX0N95%^d zZV$8;F?mC`2?4CHLZgZ4_4K7#gOLHUewL&R?dsw@cE{>$iZq!x-mfHaA}+Dh%<+pT zf5oQTA~o*rm&~sGx353^-8&DrugGMhOA!OSQcz+y3 z<1ng`388Ta6RaKI?``P6sig~JCLp2!XSIqq4IJJ^Pe@aZ@Dt$dSvWX%kxIZK6?zCA zg*5b?7XGA_l7LAiBx(4^W?aD(>yrtP#Umu%hqy+ezOlgwh{qTg@@k3zMtcTup}aDZ zt-%T)j1L;A=Uh#K$qJ;ZpxsuRF*C1Ig7X5idK%etKnH)cmUQ-CZk+B|^@t8-~VWNoWsdN3#Klt=hH~rQ-Z#s6F|6Cx`S< z7@j4X1+d{Cd6W}F(Cxz7S?I38V1R7(F-zCM);g4Zo!96EX%?8Ogvwns1X$WZk&d0}7H_z5 z{)I1Ty@-fRqLDWpojdt~AHU(-&+kdD=u5gR+0)G2hu(ej*Z$F;9e?RZA6mJge&1)m zvwH9=$1gm6_jR)uF6qf8QPJe}F(OdRI)wqk?cn{xuMf(vMKm>IV4Xr*qlCGsqk?cf zNjy$s%27zYDnE(nasV5UI*<$unZ{+ZDEKB)qvU%6eUju+ZCz? zWTn3Rq|%Z^O2`vW8JK+&@|F`-Pl<8>vHa1;EC7lGbYGZKdfN9hR()-Yj3-_~m@oqW z7~_*<79(0iBYudN2IOr-obCloQtj&(r;~#FTgW>20;FF;JE_E27pErsyh+tupc2Di z*d;GgWNjT;KMVaeWKikL$eX%KY-~106c+Acr!1as8whF-qpXtrK z;imbsm-Iz^0c0DAC0z9Ve7{R5B6{Rpx61zfvDIthaS_b#eD(CTcV9pIwM%}o5$|7{ zPZ3+zHS-1P3Z~AVc%4rdls_T?35Ea@Z;ugxHK+Ub^x!Lw}=7)G4HGv5hVj~ zEuHMu2oNAv0iG{M5pzHQrwA;cGK9lSXoNS>18ky&J~s3y``FAAQi7vC{$L11Gz1vM z^`b+J)#B+2WTp0OScc%n6@ErSb!8o5>2rHyv9TmiI|GfG1-lgSbz&%vx)v?R;dxNm5efXK5_sE)AeSvYk>sp7D97iI19{!G6l2R zJ>*lt4uvXL(4C^pzfb)r_lOgPCADt85!-KLuG5A6=Sl!tr3)FA`38k0!2NhajnUP~ zB6d_M=(S0xa$T{$ABt4-5M>xB9g@8kv-Jp)9Ko3H=yN*<$=y`x4fs=^gnd*uA8cQ^ z>$=7hOHF8Ro~JzJ*gOY)QP5##tS%sx5PftSm~&6-CokwC5wD$s;ypcSFpodW&LbMQ z{a|aYnb~ry^rN<{N6z#XF825rL#Bs0aEzKgD(*z*ZjP9>cf_+*e5@B~4l&QoZV6e)d&Ap4Z^_ z^fb@j)HKx-lU4DS$-1f9cVs6?4cxM?x$@96-9z!%sSCr-SB|gG+W7 zD#(-{XafN7`s?RT*9`!`sr5mIv@hkoO@5-I5q06XHC}z*_HID0X})R{=SO5*=LDkH zuOk32kvAI~hR6pv(M*7sF*%r6*u~Ncz|srmY(w; zcmr_#LSq~MveGM)OL}j{)XKGdJ@(h{pdXWgJJtaR4r^Q}`yDI4F%`bX4Mb&^#D$Q$ z77o;YjVRfki^o+V4PPk&r~uoCye;@Gfrg^_VMj_5LE{~*tF71iM=ds9@Fa=?jKv1%FUIPwbYn-2 zLB?BaY#>Z6Vhd>83UmJ%Qhp6Hd_2zs(v!darIW9AwQlV%Jh^`TRw)lObNiyl+QMW^EMopYVF26?nVE%F zx^-y=fLaIuqj>-^1Q?C;GTX8MUIHmBKBU3LPlN!5DpUlz5f>2AfLILyT=4u!FaW7& zrVTHc)MKkSbO+K>(RsP0YyGNJYkNjDl4pTASSTcAaapKlCQGDSVLVtn6>Jx(RzLxL zAO6vB+4wPqay5WC%1# zXuQ!5q(u1^lVZ#)ru7yEi}#SOd>3m@V}@Tqd^bF$(e8w7NSZcinFiPIPswMRoH@I} zhCRgTb3GDkD#TyaFZ+Q`R=x7b%+n8iZ}TPb*yhj`zw!C+-Sm&%bj`z8^M7B8YzqN2 z?)-r_04LV^`Sh{ty}7xgi(?7f1~gn169549_&<6B@`92?>fgJHTVH-j7g# z!JO~<#;^bR6L+llTQi3{6^*P(mds##-ylDisojuXIOnL%F;1h1YKR4jEMXxPik4^Z zjZ189Rm|HyiKT+8@9qAZ*B^Z77eD%)m(-7a^r4kkzWWtRkKeS^e8H;cc9Q8YaA@WJ zfj0nOdTL{0Fo`k9(Y>v;OM7oM63}=ZuAxt&AwUceOy~}*Nl4TM63~0ZI&3X1C_da5 zZrf4cfZk3bNmw%mB3`QjgF)~i15l)@H^W3I7!U60RnY7S=iB2l{}Xi*aG?;MUN$V& znyQL^s3lH>{!qJ+oY6Fc3*)`KA0q%CvH?{L0YuXq4vP^R8XA=NlkgG5T>$E&(FoLr z*gtRpVhmQyxlLdE`e&bfYJy! zBO|a?`o@3v@1MNq-~X*!|LBrZQ_oXA_3-&auQ}X4e`vn(>_taY2*8QB7vh{;O>rLc zg(uf0ih&N#HO}svNxPR+wKU<^!&oi|v#X*m7%;|97gC8wNkAK$fxHh|F-Zj0(}SaM zAS!+0uz^>TM4!W0#SBU6Y2fJ-jKM(2ozTnmnVYGi_OgUoZ3EWSf3ZUrpx+Q&E_551wSgg^v8yhBTwlu(g#-otL2G-w30`P3F zD*owbj=$<-_dWgE3!Bnvrx}Mk1xNQ~+`Rt~hi6)}uryo)-+2v#xp&Yw`*j9$w@{p3 z#$wsp_scX^KZ))C2h5-c(!i!2a@MhFN;>rPS~(V+GPYM={_$I{``pid_|a=h=SQEz zcUF2we*X8r{l*W!`{-x0^vaaee(OxH^AA7$)L(maxp)0LZr^k7Z~W8^UwyX6dzmsm zTFv=_A%L=S_BX$L`sO#?vT)*M`&w6IThjmbg%h`*-Y6&5l;3m1+@qKLzDzw0=*LFV zg!^J??v$wjLTP}nH18jVeT>S2#f2Uj7itB@X+aZHgMs~bcC2qM)C3ssN8Udj>Kwq_ zs^e}9HA_IDb3T℞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 Ef)1-op#T5? literal 3367 zcmV+?4cPLDP)7NiWa@G4DTH+FNzxZdGcieBPddRkcHFUB$4()k39ijcJVXHN z1qeMr`_isf+UMPS`^VL;cG(41GUp%Nz32BmzjJ<%bH2a(lhD5RO^q4K(COnO$Hy4% zJW2H05EG#QnN%{TfA2?L)~~E2ky1H)I>~|45|_hO#BI&g+_4^u%?^OC>yNmb@0gJf z@^k^=p(|Ya@OO+3Tw?6%Aj$D4Xg41Nvr%Mon}yA707)(npWxk(kJEXw5?|6r%=8)D z3zvW(-2MPEsU$^vX--o)Uz4Q8#tvA7M{WX`o0Y_l7*_-K%HB94|f%C|$l`_wF!dT-BcWmGuQ z8RK9_3e8+Y?cz2ZcYOijp1*!^Mp-Bp3L=}$7_Aj0^-3avCV(Mtv}R)GX5wjCiU7(-KzW7Q!_w1^bQ8kIC z^_!@j=VhYjXaW948$4?xi(k5Z2b!8;qWdTYsj&a+ZItSN>GmA}2y4EyUHj9zqZ~RO z<;3|I(TPk+2P&;1OBYzUZ=H>UV;eBkEyCTrgv%f7V_)6<^mmDxD70bMInmYv-dt3;uW& zLL!9Ocam+k_u2Djehjiu5-$T-9d+~wZG``|i@LKSOEH3|H!!3H=un{}#eI%O-mYv_8M;0!{H>nF)B3!J#3P}a^)sGP$S`9UMStJ}o{r8U%A6-jd%{3av-8d4} zy4{48A)!%s{=+@6=5esrPGMsqIQ&D{@DziNEd-O7a9XA^5`d^_8XG&00kAh1E^;2g zCChxv<>05GQ2;)7<|vZU2tcpx8UWfmoyE=r)L0R}whHaP&g$)JuI5zTp&I?+^!{S! zLI1txF*yB8eYVCNIhC5pkcv$p2m88MFwh4;LQ{*J&krItNi`N;=_OiIhmwfnpEIsE zNGZk6=LJDR&+|S*kdX_W`m;1w-)l;;*!lb*()y^6L=E`I&Z8;{vdN4n$;eJ`3)Kfw z07%@Nk7xBZCC%*Rt1(!tq)q=fsT6OD&Fa%e@GM#efYLh|uuyu?ax$q;3uav2#RV$S zK!%h_Uk;gDu!^i+ku|nu&LaS(jA2rF3nDnFH)q=e1}0B$TIu4IlS#c@{S4dZ(HZ9A)Bw*8 z4*~+hwq>aMJBouCjiJ8Lfv{CyHuEF(In^AkSI_ya?Qrzpii4O4!m(FjMLPiA>f#`U zu9vrK0JSGZsO}ymu4?QJjuOwS16EP517<-W+HB{2yF^=)GiT(5c^=+≠mFZYL!3 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

    qOHacftDPT7$xNBF!(WGOi1R2zZvaKx2)8>5cGw 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 literal 26102 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 diff --git a/pkg/ctr/assets/fceumm.png b/pkg/ctr/assets/fceumm.png index c3b106f7fc9b9006771cf1ce54ede5d356703362..4af02ff4acd9073f82b8a9326e90be90abc63a92 100644 GIT binary patch delta 347 zcmV-h0i^zx3hM%p8Gi!+003az3AF$K08~&+R7Iu@0O{150001nY%dWG1~MrUR74$i zUn$w7cF1u~x=1dqAP?opk-u0v+NgM~A`qPn09FS8F$4e*0RVdn02BfMR|f#PNiPr$ z5qS*-GYt*p$dY(dC*uGB0MbcBK~#7F?URM7L@^8nPnzrQ`hWl5xxS(F5g^#J9eiM} zp5ntVg^c{Xn}UQnF$F1essy%|w*QALFUqpWGZBFef~v-g zx}qD3m7r@ij|Glz1=w>I(9WvcMf*if` zSdi=X5fm7l$5en-w~wI2=sZT0y0r(}jXk{iu3P&{->W11fQN34KTes(Nvie7x7=s8 t$jlULns<1B*0NmXsAv{s;LimAkt@P_7FBvZ%e4Ri002ovPDHLkV1k4Mm4yHR literal 1302 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 literal 25137 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 BZJz)D delta 723 zcmV;^0xbQE0^kLZ8Gi-<00374`G)`i00eVFNmK|32nc)#WQYI&010qNS#tmY4c7nw z4c7reD4Tcy000?uMObuGZ)S9NVRB^vcXxL#X>MzCV_|S*E^l&Yo9;Xs0007ANklZZB ztjn8NsL%X-WSDs}6#!RRmN6I%KnTI3c%Fy#dJV^MP!!%YO;i9}bzKL~^H3B8{eB<5 z?_)ln$2Kwy1GCu-gb?Vu9{y(-##4Sc9HP-^B<2Yrm`o<{JP(V-B9^ZL0MGLPfc<_C z05~3xu~i2g?SIM8_}y;D#J5_lr}L@a{vgXTwL#i!w_7GYDByHDJ!F2gfOfmh#5bEw z0QC9PZ@(;;OQr$h)-f%%ZNs+h#C+Vk|DNhoqTneDcz3G6|NS&c0?zmS!tgZ!U#{yy zk|fmY^@p(U`&h5na9tPY^EnsZG)-JC7bd;};F_e`qJO3ipfGOpiVM{Me2qq<@E8{a z0n_OemSsg6a2y9w6k%Bw#^Z75LaM4lRaGXwR;%G~IHc}hlBB2$0YI}M} z0zSfpM1N5P$8l7c#cf{USzIV`E0Ltyq9(Bm1@@QJRep-E0)VDzAcR0sl*nDrZ3rRI zGz|d4&UIZ6KY8%91boDWy4^0dQC2RLB_DZ99LEW7&Wey{H-7-`3s6-x-&rw)*@ax! zW%8BMg)&H4z`IkrP?mh@LPc-O`=*T%*{AZH%&TKO<3Hmh{>}M}0!kOki2su=^bcS1OG)`m%>w`c002ovPDHLk FV1jk)L(c#J diff --git a/pkg/ctr/assets/fmsx_banner.png b/pkg/ctr/assets/fmsx_banner.png index 6a175d23073dfde82903b2c6702de6bd1d007586..0463689a6af9619a9dbde601db36b093b0141a7d 100644 GIT binary patch literal 9057 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

    jfcP#4 zW8l?IX9tqfF00hdFtER2ml1slo>iX+MV1K znr(UkI>aB)(XqPHq;O5&USwW;rR3Ve#|K!%LP`n>#L&X5E47~k1GD9P)2EqjK-U9HGKN9wbI6>rU~{SfhY3vY5gS}jg+nzFexfE2 zQ1Rd8d+WQ*%8DioP&>vn3QD?6I@AbD(&XG)I~q}4*$?OY!>ujnTByVQ8UN8D6z1R#!z6mzz`$y+>sX-9D;z=XFK178yYNmGA^6{22}8!zAQzw zDT2MM{_J#$H^NkjF7V20M{|^YR~S zd^g{VGWaEd_)QcuGjd<0?^j~bQPhh0dB+QaUGs?_9~2dVG@P@A2M%$M-l z*ed`zwbTt5l@h}6N2;GaFejxYpWC#aLqj!YvOg9b+)M6sF`SfM2?op|Edo*0S4PFD z|M~80i%{NP`q8Jx`)XOz=J$mZb3d&0ZO0`_#QGlod9_PyzG)@`4N9jgTD>oa>Owul z7wm+ydu5`_M-4ut#%gj-*K%_?G|xwHq5NUkdpA8jPqaFQ#S#r?c3nngRoAWn>iL`t zEE{i!DQ}nz$iu+7wWsjTMTO&t7k$Kg4#=?Jd7K!OX{xY7_PfOW9RgHz&{b9#^J@2; zcciI8!|C-qBNYzO^%(l#ExS(X`+#G~J(MgBI}w9O?z}q-q8R`=jd;M75a6etmts1D z<&p~zqJw-Nhfz>crr zA8L6LK3a{3;jqj>i>x%2RoWTP3|>^s>0li=TG`_GqFfvPjo{NeaPLz;W${*dWU#Q*>R07*qo IM6N<$f`jJVQ2+n{ delta 1869 zcmV-T2eSB;1knzV8Gi-<00374`G)`i00eVFNmK|32nc)#WQYI&010qNS#tmY4c7nw z4c7reD4Tcy000?uMObuGZ)S9NVRB^vcXxL#X>MzCV_|S*E^l&Yo9;Xs000KgNkl>O@Br?JwAcz58!mVK8oeg7i$QDp!Xc;)#FpsbB?a=9$gTG#anYRBk1hx zVw>B;%*+e{ze*yJz;3rQJ3C8JQ4wyB2ahCU1D@WqJLf%IE)kC`;}k{8%F2nwqJ%>s z{5~Jy@N3x(9|L_c#v@|29-o@l|M~CBdSlae{p{Z^q<=Yo=G=L`Y5NcKf4z5Ek55hO zHBM(*f3YCwz4N5k)7_&N3qo3-t!NIITkjItCd;fBMM}%ciN~T0he90o`FK5t6N85i z^VpWhdEqC|^UZI4n~I8xtZO2X2*IHjd9tyIqro6elG~W;IMB=1dD1H^EaIoCj}~)! zYd|-TQh!0v`{!}uV0VvRDhO$P;}d7}_|&w%x3w*AZ)$#Qe8TAcvc>ei=yV#rf{@|T zbDdp$XC5cMC&?55KQcK{T2>B#-EPl$wo{dwFD)x4we~hy1|Z;9+4a;9v(j@o9PHS+ zi$j3`Pc}BOP4enw;xS#m&!5s>yM(YW8dayV68C<>$Du+jUbrty{f1|vO(!;!V7IlaFm(}Rp>;7UA3g@toG zWjewo+C-~hFc*W>nh*w^09j-9*GM5NMdYkL-e zBLUUuPfOsGrm?|-6Q@j00C4jrJ$rWZ``{1_l9$hkE=nsLydRD5mKGwYDvS;fV}rE5 ziIEYG_*Lo`kX}$xIBiPLR%v>XRaR9o@X8;!aU(IO6McOLjhi*r)iRp=7P{543x5JT zJNC0hlIOhUmx{vkEzQi{ylH%9yFcSiysNv1uS+tC#7s^n+-?u63JW=+DD*$QXHhn9 zu6vzMwn$#?7hSBaa4;2(@K-IwQB~pX;o+?7ZC}U`U>5|MI`;FmJWd={6b3S!0JzU3 zvRRh7PZarhSvgl?QAR@{e(m$|c7HAqTZFPbGzSY+I-NWwdAV0~aa)ChE71s}T8Lw+ z!dQkAc0r)Avx`UFo;epfrurCcekM!wEejAA;XiXt2YT7K01*3)>SJIIjIMNvY?5Uv zMUgdS;{PkOe8Z&5&{Sj~@*|28jv^!FWPZ)+=sg+(;i)qgT>dhqRn zfVZ=Yx;*I}SAD2+q?ZMA>s%rmWtn?SPK<>@{LbfNJckpTEP~i3$;&yDfbVy9Q9Dn1 zUroZJFGqTY0J|XYL}wQpGMp&8bc!x>Jjvxpz}B=G}aomg#bem;1t zlVn1{At+G&&Mv+*kAD-j$v+1Vo19o<5kJ1mlSGNniq*#E z=W}9%Boht}8OPviJjPlJ`0*bTe%x&_y>%vxjsx$Kq|pr)F}l`-(Pu4U^jQ-|ueO-p zY7<6ZO3oDsz-KHvakmMh|7a1TPnajzLs<%RliDN^uu{D`h=ozw52(% z@6Uq+JYWH%wPbn#tALr5fD4+&=PfvKJ_kl0*|VEd!69lSFP|1&tgdiyH5%c97UES^ z;hh{9{g06mG`~vC0)NtbRZ)1?gweMH7Xh0G^gFAn*lq!%U#P3)okhav0zqJN$9}{- z@Te&YFSRuDf$9A-2^<4#-XuzF?(V_00E`YP3NL*G7=4%1i70vbl<1uoAG)(>bh*`vfTb=WKRo>Z*YZzbxeNF&Lsh|{Y`&?+00000NkvXX Hu0mjfCJT9} diff --git a/pkg/ctr/assets/mame2000_banner.png b/pkg/ctr/assets/mame2000_banner.png index 86f5e192d0110120631f484a8239689d263d8d1d..a44c77e104b631dcbc54750a6cb1cffc76055ae4 100644 GIT binary patch literal 12563 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 D?@*7= literal 31430 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 aB`qht{4@sM1e^)L) zb2Pd9epouX@M-eagpo*0?Mq!MILHA9|n>sV84r6^?&s$dEvXM zqQrmt1W-fb8Z)mG4TF0SaVZr|^>Ad5qeE&awAC8et_NMGDWzVc6 zD?6(QWv^r(5|a5rGO~rt%HAV8JK0V)+4JCV&hP5`^M{8=^@#g&-Pd@%U$5tTHzcWp z)Kq#`Mc?JFSD?GxuJ{TSEsnPo?PUP^k9be>F8Kyq(`Elvi(-=jHJWZJUhajD-`b(u zj6t821ZLj$ZD=S@^tazXef03uUUH^-EhWo5-DvE1uC8CzZQyP_K=_v!CgIfn zLs+woZ1o$WOG&m?#r`27q5iY~8ux!mlG|85jIhFn65P9_(|y~ArQ_>J)@t8>ES8*@ zn82bYgtbgJXN;EVV)~bfRJ8g7N0RQ5kER?} zRf)zdGPnEj@MGvB1ZbINFy5G@Qq~dd;kSp+N0cLtQ<;ObFU}uZ%j)$>Ij>&u5ar(= zp_|!MtsOlu%M}B{IPuhS)fK<#R3;&wyAmgmcTJzu1MRSfVN@TU4zqjCoO8nlV-OxT zTKtQ0jHvE9Afr)qm^b7piJF`#*;? zUrJo6khMZIHR~vMxcV%kk&_oEN!K*9C|=}qvvKW7Hwi{yZu(}WtFEV^p2s2Z6`D>F z-}M6gi&*tr5e&dVo@SuMfQ&jj+s>Grn_$%ui7vSrQ#q@8GMwLeLtM*sw6mQ8J|>lu z=aerg8;6G%k!h*M?#1hytXHlWIrax4NzE>@#N8%}cU?5BU;E}Z=qC_Y%Iwkk<1Nv*cx^ zI^N;F^&a*i=T1hy-BrFZ>)o&yuYX7sp4L#G#8WCYeU%0v21-2eE`GDf%2aD@YHGT+ z0bbfh4Ti_FO#SrlYuZ{5sROJkiTR#i{r8Lbri8R)djDplf3)7XPMz8c_8o^U%&%9) z0RvIr;V3v6bE#ChBS)EXZ{AnQjpswKDDmK`;2Rnoj z%)R{nk}hjwNf)({`Z|I}fNY^659jf@>Y>UPCD9@H+LJ_njL2Jx!+TRcb3_D>XrYsn z6V2kK#5GR_P-paDRRS}qbszXW03IPtTm7Zuil0^3R*L+8XQK8QqCem8p^FKoUbWf# z=WRZ^-{Oq}=@?vo8G6Pg#Zq)jW7|e7LVb#_ic902BKgQj$IA0x^JLn3WL?4dxOasa zY?o}~UHY!p>FQpIMT-Yn&f#weN9EBgDtBfcL@HHD#}hDyNjXnxzD!auX8v0J&3WzU zSMnswec8{NlVCNuo1%1;txQ!O!-rbKv|<+?-h7-hS1V+Fc9a^wza>2a+!48+eqjrN zE3rZLz&LX9e%o!c-imBfJvuL?X^o}cn6b%cG=@#kLhVhsg8N0Ash@Fnnm`&p95 z{u-vosYWS;{ZYMH#n|?Cjiht9h2`vr5G24}UB_7<6ZlPkXE?X3Kfi0kJ&$kv$LAtR z={m~*2kU#=xyinIKfQ0QB-=QAPY@)gCBfwk53j8wRHZTyPgqkr5=IERs}sIe9d+)c z3a)p4etBZt^lDGL*^5&}ESvl%*_mR(32M&iatRe8x}6xwrek<0?Xo*-`F-Q(jBODQ z<&w3+Wn%ezTA`PL*zt!O0a>Z;ygJD|WgxcKoykdecc=t|yBI7_-XDtF6k&CJb#rd`ebizlv46B77d{D~;T3+QFN}@gCM|C&yPWr-< z6UlWVSL8<~p1$TQOpaUmp^sBO{k}VonSnvM#IO_C)}9Wh6GXA0k#IUHkM-aytS=m#Ct;_VMbeo zgJmC|-kA~N`7|sTuyf4&K;m1}pEIpjd-G|R@s(HTM%h<8ni3s<$ci3fmsZGqQQnFx zQkV`VvWlS7Z4<=8!=U=8`TyENG@1<`{jODU;p@sH)kP0N)shn}_G?tKHcj9fOI8tc}Fv|2jp(S(Zn6 zI143sN;z@-^*DtJPtLlXkk)1`wfn}cj@ui`q_$XDai!{zx*S^2wb zq~us(Y|~Pc43M$2bei4~P;{LGy?DVkLs$Nkp+?&&#UwlUa;H>B*5%dQkR&Q_J7bEu zJ^;06xGi?7-qo;lS?+{If52K@*ZMYJk4d`Dh+liZTyy!FPchqFLtDT_Cl8%$VT0Cs zJR6)LC)=>9t4do5u_RQ&wTpCWl)&7{0S9U8H_*MK6;`@e4S!X|xTGpYMgf`ek+inyzBDBd8JVca?GjZ}H z3X;W1GE@zfiIn30Zjx~|PAH6DP&4bzOGzArcBa?MH?Sd|e^03K&&1E{ht=@+>E5U4 zp!C^!@#}$bQrP73!@R}f7e?ZE4Kap6*S11T43H^2J=B$U&}K&=AF1+~4s~{sbhdh;}BkdAadi z#vCme-qaG0GAn6l5NuVoi9r(+6Pr6b1)dEt7Z-ltFXtM74M?9_jXHv3C)iO-n#)#O zU0?OGV3&AI6Vh6GqN$V{CI4CQ)?-OVZ7zA<@Q(6*LkZ_VMuT5Uxda~`2yEoS;;eB5 z$-_x=1QCX;f(M9s<@PWK3aI_hIp6CoiNN@TBj$pS;oA*L+~fn5u!n^RLzz7l!xDrA zog>w#b5F{fOp0M$4#(s_GA)A@AIrPhPhSrCb#NepR5itW+(8{-*a862;z>wIYWFE(_u0v7#}6w%q)uR^wGoqosfGCHjgV%$gME%yWG=Hf3qQ(Mn_F*O15v=)P& zw{!0OlU?^qEX;ZSKb)PPeB~2rgT?-AA!93IEw&jFC9Bvh}r5n=O-i}y;fyA5Adk$|j$($P( zZij+c3VFh&YTjk)i~H=ntmtx)-}+A~C=vG8xq_uBm}1UQ4|CCnQqOP>Z+k#i5IfE> zkMd7gAdZ)XUM|lb9WQ*8H|&vd2mO2c`h!fegJgpBMjOYlM95bzC!e6t`^^O`!HD?b z3o^oS8xa0a@xWIn)q6pnDQZZnT3{C=^gy8yz_Bpu-?so!#^5?^Bs>) z+i7_SdYV#$<*Tj?({8M2kNdjQaJtM&*_|R+%u;M=Tkz@R8!s=fcg^p4SVQEz`qk*% zE@!9OFldY5{reI3c*mnFG+&exag&&qPEPkKo_)0ij-*h`mbXmvPIO4~Ct01_D;TTB zm3WH*_TovToJmD!b)pZon#|f%{pyFfbgvpqh<*K%=&cl=*wX6A@bI(AFGe6&G5a3{ zB?J5qS^;>QSI~9QN7$PMY67bcF@Qmqe|DqTi0$|Jtsg@1-mld{C?88&64)?2*lz1& z78RlKMfcm=ep8rY@V#;N*v6|#S>9>6hb_&D{Uss_zLJ25(eKQ<(LRD>VFdr|iP(T< z4Cj@qC79@-!p4;qW&+0#2rWKW2JYw1cFelgPcnchpUYTDc4nQjVglVZ1(G{l}g zA_*=abH?tyK76TzZyN%I1y_%s?*E-UeWbzs)g27&hcNchhBEYa^-a&=i;YK_o`2W3 zLK?54V*wy1QM27|XH4VRB>3;+H`$jbDYpX9enL}YjS!A82{w9ASn=;y4Yuu3f;&Ax zR}VTryHBoIU9CziVhgVdC(ij{$nbZ6v;uKGzc}R?r><2fCDZb9l0Q8&)0rHX7Q_gA zd77wa5)p;1nuOVytS`$h#x)40n?)H}`e$s?Zm>k=$8pfZ* zP%5m1TCW7^mRBkylwiy;XDLJF)GwHt5>Vpa4jt#K;)RFb{?TfgadP5a!#94PTrR=C z{{hc#YVz-v|30t{3+VWD-%Jf&zDcfNU39Ds5QLAf1~;&~dm)&e4Xf(i&lyzOSCzpR zgBTB0vm0hI?EIsH0QFt+a`|{Sv=^S$_hG@DpLqy#GBSs zV#3d0|K)Gnga?r*7Z0mNE(6>5m&uo-de$O8{Y&?VVPY5iXDHOfOzPVe6DXV>vWtl7Q_3fl^&^(Or*I}iwRy!Ja|2Ay&iQF z^VHGV%IS^|z%Vm;BbI+J4iWtc5My>=E>5*2 z<8baN3mU?!H2TAi%U4M8^zrX}qA$rBe4b(^WF2OdX1}0AAR{4+9)5M|pidYGl`)2O z%e2dM@HfE}@xBNvg;TB%;7pb!7z}Vxw*PsLKPL`n^FQdg^6XV)8z*8hdX2*K3La6# zVAvE6BPCmwM2tlQ1?hGEWHs%ZS(Z*}HVNo*-@_TTDpo1|L7$(EJ=ti%ZhWa^c0e}X z$0y)XL{(ysn1=oAJf6O*Xv_3^x)q@ee<=9TAaX{2u&n}r;|g#3Clb4d+)26qEv`fX)vvQ00s{p-VfY`We`LSKk@z(w87kIOA9js@ z`ByXuoNkvd36JMI6@0^>|gU76VP2~-qw(G zleO3fQLsf^@a8J4f3!XOJgB#vHBJ1BMRsrV@@Go6%mc!w|B+lyReVC%i3BhGW+zPN z|LKIj2os~_Q=^NfOZsihnZ=&jM^kmnB}QywFC(g&LsW(l4%;&L<0~>(V%PAxky|z)ne@VR~CXKD$#NvMa zPxexFa)V*bAx5k(A&MVb7JNuWb(;kN8@I$8^Wl2^oL=rfn`I7BO4u_33(utki6~x~ z3^TR&KD}~f0c@R^KT4;kZk*R(@Y*GC0#5v?G3OrPy)Ma_)-s^d2AlH93 zVjics6uwXkgAo1iZp6naT=G>8g)B8-JT7@{WCq>a=j$q@H z0HVCFvoi@`!)|VG-x4K+)i!0I<>CCuV8^&;O1EQ^*heuN zlKPT5=c;=xO?7{6l&dA!^iE{gkI}}>}+It4c|hxuFrC$Ov9n+ueQBP z<;F}pMH3@Um5($(Yz>_Eq8!|yaA)24ZC>h^)9;g9DT6*p z-;CWe>HHK-lDUiTw21#7jp$n2P1sMTP9I!5Q$(l&U9fO$i9E6CAE&PYpNWRePAs|> z7F%04qw?bdPvTKmOExW)1|jb=Gj+AMrFA2X!8{t_V9ykWZb9U10}S5@U)`WWwnk76^`{9 ztBZVIt<9u}>%Yf(A^yzkZ&f=mKBO4pgibFyaVK(4k z-WbU+xN$5YEnSlFrkXPJ5+~Q@-P!e%2TF7CxgXay#91Ajdm_Jg*DLBXiy@5oOM-%T z@vCNLX8s6RV+Qnzxdwa+?hrOh$ng+ZUE^LckQ>PSF~pm|nk+*%Um`SaGAj*ujjmA{ zFuXI0Jjt_oKia?TE}eG`^F?}g;I&~l<=LdC7wdL=GUEKMx2+ZZ4LIJLPPc?rF z=s722cSzeUedp60wL9ta0fUnobu>!|eg6D8jJyTgdB?%tUOO*-1HHr}j9#rx(oyh} z)c5xxm^lS5kDU!vOX4ft%$N7ZUZ)tg{K&fs^Iv z*(km@7XX|3!qam+8Lz40z4JY%d4ziY*oW&#T1llTk?lz1K)ynCD4jy0p(R~=Onyvg zyQf@eCG$HNi(f|8jekEfg}gr2b7{>22Hh3Juw0{;bV1XIQawhnzT3Kk@-D(nDVa6= zaT5zL32*3$9tI$mIoMp~RevWEioby8#aK9y{UZ4zC1)42J86e>xFr&BG01bzb-sEV zgVuIln^;|EBd2R6b4!W@6tA!7ixDEO(cxT5F`(Qkk8GJQN=$oKd_AII0S{?WUaJp?!S92Gkj z-Ya+>o}pT|7YHJrI%3G<<&OFq-z%JVJXXYZi(q~pUk5#d|Eoz{6=Am?S2D6JNW1r# zIV@nvQ-eHvlUR^Fupf$L*Wl=8pJa_5av~4tUVq#}E`KuWaF^6{aMJqvFXhDJiFedf zsjVkmotZZ!7`6!R!ZnUENQtfwZRHG#{d-M#N(0arV2kFh0fm_no zQ{nhD1PSc0-AK&UZ-Cld!4^%tpibG&WND)4|F=CcI;-3n^3TY7_?^Rw)S%@pekwEv(YDs80Y z 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 delta 1417 zcmV;41$O$Z1dt1m8Gix*007uvZqNV#1y@N#K~#7F%vl4FBufyTZ`-zQ+dp)^3!H7+ zwr$(S>KnJVw|i}C(9`+fO!w5(_H9q^{@9M$jEaiftgOlxm8jev%SxZLaDX`WE0Ue0 zkR(YVO(XIU_TmJbv$APqF;ZAP_&wXP75^U)_TmJb(=vcSYkw9UZGR7CCKv!x%xnI{ zF6_k#IHv^weQzOUjQX60yh{#y^8i5~yRes?(_(-yaP;mOUO=#CgbnxBK#kc=D7BhHA^pqoV>-!ze zdwXU2EmIOPgNKB8vi|E3-{1hD!mL|kd+Sx(>o4&G5`Ss^8mtmW4%s;~OwFk+Vbrr%V)!s0Ui>_X9g6J zLgjPw6Mwp^fZ!M01Z)q#@9yLmpJmsR^y0IS)V}s2R-wZ@g`0rwt2(}Y&EB5g8`|5$ zB?%acRp>BJ;U-+R-|KGaf6;qG{VuvBV`8wX`mVTl;^mPFAQ`a&^SF;BhK9ogIT+|D zHV`W?kNW@ujmsc`9L&I^gNia@1?F)dK%j9MB!7^D>3=kjKzU#N5ir9INXkR}!X$iz zSu>d2-lcrzq2x(Kv|C;J+Rmfs4fsWWz`2X z>%sf1U_N|Dv4NbrCP*Jp?~k#1$FJiQW#p7)T%0bT&xd36)+vc3+eo->?Hc9js9fp) z8-KE=IK?MDZ&`6a|Hn|>g?iyr51_nH_$*6mUY7M}Mwa!{&$7yOr70a$Kq%NqG**XJ zJn|rXG|N+mYJJzCVXiuaxKZm+fbTjq89qYXccwuS*d}EtJHCKzPfJ%}(BhkpeB;K}t>$27gh`G$v_9L~# z*7H#AtIN_JotyIH{FD)=@x;QW)n36=PK!)GX~yYJWfG z1Qq*eKY&%6_9%?zwI7$W>tNMK`+=K-H(6mc{pmog7(N}n4(GC`!}P}$@oV_FzP_A) zTup929-Eh9*nT`dFNL)qrZ;G>ElV@JA>1Ns&`fV(QF{zyXyj>kqUC+5wy@yMYd?@d z$Ri)^2Y9tY7WZOJzKhIjKTv6Ww0|F{jg9;p1TwGvKrMz<*w(ZkxH(|OYUBSXn%#cD zFNhVyFLDZbj@kon>i;Xj2Y=jF?c1&MMd=VcX{q@n)DL!42+~wbpDb4#V+<)ak>TnMw zhc@^+e);lc#`8iVXhI*m%=u3J!&Nl+9oL6&5D!QY1ci~^#Ds1w=wlc5ngEFXw4ec0 zQ&Xcbyu7xy7Q7ihEwCS7jysJy&fVgB0zFo3Y%y?X~!2+{@A3p8OO(EtG4 XzOHq!I&V$@0000E-Wj4GaRpH=1;Y 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 BlSTjl literal 20645 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�ba}ds>-h7AB_FlPu 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 Q&j0`b07*qoM6N<$g7&mZaR2}S diff --git a/pkg/ctr/assets/fbalpha2012_neogeo.png b/pkg/ctr/assets/fbalpha2012_neogeo.png index 444890bdcc879b26cf657b82c75314c60f3665fd..f1b12857d4a65bdb9cd974f1c1d74d69ccca050d 100644 GIT binary patch delta 483 zcmV<90UZ9A4Dthz8Gi!+003az3AF$K09{Z_R7D>j9UUDTM{rCiC?p_7Ss`hWA$hAC z8yg-T8yy`SmX?->hleR_OD~ybGOu$>OG;{LX(drRGq!j$wRn4DCb5hx%bqV=OC!{! zFoG>DximG^I5~hUEM6xj-Z?vG{3GZ9000VfQchFFh=tm&eYG_vjfE%0dqQBssu zRTctG_CE%S>ZO2oU?BKDp4i6&QYrHN7wI2Fet}S_10}RwG*E3oJ7xxGpHoSUIk6KK zQa9E%wL}knHh<>APWW=Q?ZRGT*LpGL%udVqxAx_BjXd1R7}*5Gwks>qY0wTp-70l- zd~^WN=s<(CBLJ%d7Vo73()G^uoq&>+?(VU0cTjL>x<6J-1lLO97;pmGJ{)xqr32|{ zXuS^f(DeAo>3~C8I>5rl002ovPDHLkV1l>&)ky#V delta 1554 zcmV+t2JQLs1DFhu8Gi-<00374`G)`i00eVFNmK|32nc)#WQYI&010qNS#tmY4c7nw z4c7reD4Tcy000?uMObuGZ)S9NVRB^vcXxL#X>MzCV_|S*E^l&Yo9;Xs000G*Nkl;u;i`xKs+Q8w8<9B@aU(7-GEkuAkF`$;KP6*E<_0D&J}M zpZ)*&znOpL|7UCx4Eibl|2Q%=HS?Wb1Gn0qSj1o;5cou|fvm_3xI~b)9U!fn0C!VC z7M}x++yb2(0e=+9iq+@29NjcKI~yq$R-FI|c7gQl1?(>U53&mA*jdo&KiO>){CfZX z%%{b&s)i5=c7bf)U*35Du)9Ed_k#qwnC(-FkNtjscd@K`1YBZaHq+_$8n_pia;K|0~$}2=+MY55Jm!Yc0zsj z`vdCp%zwT5NT)+cjcYrhlOrJQ3_DRq z0q%~dufbqYeV&?{s;EvWI(q*W*8tzixPt(2cKVRKtTU1+2{e2aG<T>{%$ zc7L5Jt$j!xJ_cbs% zk(;x0FhOsp69fGs1gCb?HniBVp+!L7HWyBulW=_^UD1{rLh0}WW=GOt_QlxS>BQH2 z-6hT!LvRut-si^l4rfJsDgk-;4rnT4+J6x$XP^vEn*JUrgv~&|2x1~!2>}NBMbycUO+=HH9n2_sS!}6{7g_e zgXcevAbrzZwk*cmubWGf8l84y0wuLjYJ3{$yN@C4dJWEwR@m0M0W21C%J3xf$bT=O zx!%UC)6!@hLb}x`l@uf|hxL=xa$sSrrWHb3mw<)Z+!P*0b9i+f+Y#hpG;i3hYt2k$ z=;b){i*@MXU1+rz%*dKo?y06`Y;aVrPm1w(CW9~M z9x~~$fazYS#m0UM+n*J+&fwTmt+rw%3_l7GsKxiwSAPM}7gO39M9!yUGuE)qUoi)89d(tip6H|;+z|116x{ul7i{6E0+NKZIF_S}a z376N`s_SY;C*XDt_VDScujy&ed@WM4XWHV*@;~kK4(FB@VLjd0XjmoX<^P8A3JL>( zz@U)e>A7D;CB+Rb%^rj#+Pb>@{QUoSyI@B%yXPO`XP|zT7|1EU<&b$v#qsRvvF#XG2xQUbB%BX{IMJF9#)&s4|9aGOBL{a(Fu^ajYUI`nHV# z0V8lTAdRpgA!X+Uo0yeKx*J{BfGQq$Hq6 zEFkk3%KaDxeGHX7PmniJAtUCK#8bA!G4Ok~Ld1(BvQa8h~x-7mpi`sKGM820m}zjxD6eGdeoVq)VF5|e@{si9;R%S~u<*wV1R^3X9npKgb#&BRoROV}D1cQV?rw`_9uX%c<%q@p!M@s`^^J=y zZ5_QkuBwfOjK zzPx>OvT%Ae(1w^IPC$)DY5Q9HPf48+vBfeK7p1w*aB*Fjy zDl!FWNlh={Oxk4JML8~VnaKH*qu&N20ano2VW0{HcTV=Q=4Q4KfwzStfj+B1tYK^yya4D}c-k0n*PS71*T| zcU))}S^)M=5ta0_hx0Y+VpR{cRn&tAfsT>XXfdh)^&sh$pF#aJ}#x9K96z64yYFi z()^Rf#eXh=r^tTD`OvC52hb`4@J_~_arH)+&^Jo>-8HICkpSq9CwpF@H^wyM-fPle zvlG|I@Xg0F-dpFE$jC{&(@sqO5Mb=F$Jdc|KrcpS&FaPO9V}_RJdhZxu%Y$N7Ame; z8NRi^ll>q~z-`X>jqe+O*I}BfDBFdzIZt|BO5g8c_J&~T1*LVlO|=3|zDilnI@LhZ z>i(m&cHpXRoXqP!$04OKj)&U%p14Oh_q8=!$|J&{7{_ueXeW!UatuYEwyRvL#&{L< zD5MMwWE0%tWJfG1txrWBPd)T$&3$yaiv@TxyY=Zaq{QA{NYw8$NQQ7E-tFx%cip|?tYBR#B`Qa&W<5w%-HIjVRO*-w=xtl4M+b^F(8Nh}Q z8UD;3l4F0~XXtJxfs4g#uDtge#32FcXZ6)m9_80X4@2pNRv-`_fHM{9X&z3gsEF

  • H#^J#$pz5z(>1BfZz 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 diff --git a/pkg/ctr/assets/mednafen_ngp.png b/pkg/ctr/assets/mednafen_ngp.png index f8e4987b125dc9d3798846ed623e27394187542b..70cdd30f09581896115825337dcf26fff2104a00 100644 GIT binary patch delta 628 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 C1wk+H|wZe)|S7oEtjlg z_$tWbOIyS5)yx0w-uWuTBTdIpU)wBI$MBhh$&WcR?yp)ixu`19FVf4#OGn4@rN7J1 zEt?(9T=cXpilS2VwJr3tEcIW@_X91GFA4Gs{tpKA5DG{-LRqhY^t}HC7t3xvyvxAA zsO;(D7*fIbbPDUF!v+E@?#f(?6<+-R|H)uWsdNBWpVf<-SdhSElbE$R*RJ{j^$zg1>DIL>|Tr@%^~=5n$6pbhiNKYR;% z!Me#_qR;K{!5K}(QfddjD4bDx-ko}C>60xnXDXeRY;6CfdE>xUt6aV%U%S{-PGxNT zD`4~d_1$Mj@@gxNO~0~uANRDFC1+VP9VbO)ZDfCNBR-Giw4JAJ>5aNA852@8c|EdL zy0Z#coY^#`U1h7ugSjUHCoVNoTDdgpqvgV#o@eKl$*ewXb*+7C;iF~eb$D<5`0(g< zJ;w{%U3;uknZIgUEKh8*%Dug)^i~;1$un6Sue-C`TW^*AUTMj2`M`eBIL0d%tlZaH S%}oRbE`z75pUXO@geCxR&gj?x delta 1349 zcmV-L1-kn11I-GM8Gi-<00374`G)`i00eVFNmK|32nc)#WQYI&010qNS#tmY4c7nw z4c7reD4Tcy000?uMObuGZ)S9NVRB^vcXxL#X>MzCV_|S*E^l&Yo9;Xs000EaNklH_H12OK$8Bh zPM-I?|L6IizVA8DTe11+9g=xCt6m3Sud;ekV+)a-``~=7_`SF;b%^A(vgpkJGYSSC zH&46)eYQ8=yMMXz%~G@%G?2?1CL8jl`@}Dyf`P})f_tD^M%YP!&4j3wG0IR;nD^tI zGb#c<3T~mJmlE1BF(!)Uoug@B6%h_fKigz5?}ciiAh8g`B4gBWhgZ3c10$y>V_d_C z$kYop${_E`TbL40x}wg97NQR$$$Ki1B4sS{)bZ8M(%!#|wbXYN27>8d~i2to(Nd z&kx@bou169@LY6N1fM7s{6z^!W@5xP_V+uc?lZ882*=fFOsE*v*HQMyQNH^9PtJDr zAl1z4UVjmSecKy2vS$~*Z$2k{WoX{wm!ca)@V2mVQ|JLsxGr_Pqq9}5{wO)=E$L+q z5%g6nu)KMU_kTRYAAN(&w`V1A`T9);#wU64-~qy}Zq&e>=96EFt}ZZ3rHFA}m;kxS zVqN)l%0RV@a7g+9s5pF-FFX4<*Egu0;Q=N+K7VGTD!cbIGuL@8<30J%T}2d!FycjD zT%=P*Q{RD|1c3O)8ZWKj7e`OlNs|2B(app%DW zS{)ZP*XDevQqp?Cg|XhAHkMD_ErdxpcrImNxr_o}sII{|e09-Dk~oJ)G1Szh#q+6K zNq;mMHm3}fCU%t0Y-QT(&pFYQ_cnU7B`uy$U9(hE@r=EY5F%4JI|Q1`(}O-sIj)1@W|z~cs_OABGJML z##07*DFGnh?4re9kCMaBtq4I&(-wlxu79+#eCqaM06fUN~@o*{I6!jS0=RMG=CRcQ2UiVa1Pc;`^6~V`%obLE48PFGppK&^!k&FkL1{va* zoVn*fvjJnh9mD!s^cE`+KX`;khJOe;J2S5NY{*tOfWnEyfkwhDMHk!N+v^wsjT0Hb zN*U#KeApl1kunpV7&)bwnLr;eUdnyF{hb}jaZLkBp5hKWWspsTSxywQe7J{tpHP@a zj|&6s!kFB!06kLZU+L>RcV9!b;J*@M6f-7@?*^984dlo*0iT-7i@$GcYd(uUIGFW+ zqmKqT2Vx4zO`*v1Lh@@_xuHE%;ROm5C{Un4fdT~zJbwNI*HV8OPzkZ?00000NkvXX Hu0mjff2N57 diff --git a/pkg/ctr/assets/mednafen_pce_fast_banner.png b/pkg/ctr/assets/mednafen_pce_fast_banner.png index ad32f9b9b4384e93489806e704c4d8df8f47754b..e86d4b38b00db6311d1472818438430122b675e7 100644 GIT binary patch literal 9129 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 XZf}2kYn%250x;4yJM&K0DdztH@DLZN literal 25219 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 diff --git a/pkg/ctr/assets/mednafen_vb.png b/pkg/ctr/assets/mednafen_vb.png index dae4ed473f7fd5c81387adf47c871813fb88aadb..ada5e8b68a8a718732a1c7033a9af960eb0f5381 100644 GIT binary patch delta 401 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 AaR2}S diff --git a/pkg/ctr/assets/mednafen_wswan.png b/pkg/ctr/assets/mednafen_wswan.png index e62a1958ce95e60f73343cfc9e038da8e0cb74b0..2e144111619bc20720347113aad8ee7b31e98049 100644 GIT binary patch delta 600 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 Lzj{`!WD)XzJj~he diff --git a/pkg/ctr/assets/mgba_banner.png b/pkg/ctr/assets/mgba_banner.png index fbaef75750cc6c1115e8907a71d7865baaf14a71..46be827979a445a229398a52bb6421820c393dbd 100644 GIT binary patch literal 7827 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 Do0%^J literal 28426 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%0DV@aV%A)%s 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

    iVu7ZS*7ke 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 Sq#OKh9blkqqEn;g^7MZpk`L?v diff --git a/pkg/ctr/assets/nekop2.png b/pkg/ctr/assets/nekop2.png index d7695ba10ecf0fa564f0b4a50b2c19241b9e7ba2..3114361c91aef64f45b1453b72fff493c277c7bb 100644 GIT binary patch delta 292 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 B7vBH? diff --git a/pkg/ctr/assets/nekop2_banner.png b/pkg/ctr/assets/nekop2_banner.png index c97662eb6ecc45eb7a401fe637660ad03fc4d4d4..13be556cf4dbf4f28098dff6e2883070c1211e90 100644 GIT binary patch literal 6714 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 diff --git a/pkg/ctr/assets/nestopia.png b/pkg/ctr/assets/nestopia.png index b27ec6a51c6a50f536943c9bba351d0565705afe..bc1793a05f96c4d76203e62dd3128beead3e44cc 100644 GIT binary patch delta 326 zcmV-M0lEI21=a$P8Gi!+003az3AF$K09Q~#B)kK_YUh=5rH zW+3;(2?AmfSn`Mz0kH_O@#G>P7C}Cq0tCb&2*y*4fLH`JyCh~2c;l%+KrDN#WrcuP z1X?(}qFO`1EI$G*j1RxGT{NK`0xe0KJa39>ci{H=&)b-7YAdiWL*Bsc=LE;;F60}6 YHOt^8?c~E=?*IS*07*qoM6N<$f(5vVl>h($ delta 656 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 U>da&O-;Jo6lBQyvyk+?R1Mzl|nE(I) literal 27692 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≷%G`K+^)>h%TeiNoxUjHws-WM?`2(^=e5sunp-2 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 Md%CLS%C_PE1L)cEb^rhX diff --git a/pkg/ctr/assets/np2kai.png b/pkg/ctr/assets/np2kai.png index d7695ba10ecf0fa564f0b4a50b2c19241b9e7ba2..3114361c91aef64f45b1453b72fff493c277c7bb 100644 GIT binary patch delta 292 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 B7vBH? diff --git a/pkg/ctr/assets/np2kai_banner.png b/pkg/ctr/assets/np2kai_banner.png index 83a82db0f6faaf5436c1350a4f4816e33e18acc4..1321318099513a5e587e7536f6b0032d21f25192 100644 GIT binary patch literal 6728 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

    88Zln3|JUYJ#6F4=ZV-0dufAIBz$Vp3L(HTd@C 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

    }W-gi=E$ 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%0Aj`8$LanQ?(4GNXJb6ZWsF;c_6njS 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 D5DN_* diff --git a/pkg/ctr/assets/snes9x2002.png b/pkg/ctr/assets/snes9x2002.png index 34ce4dc24ac012e34e052fcb5412becdaa284b91..6c0ae34f6b080734a10e7a8ecc53413f97facac8 100644 GIT binary patch delta 753 zcmV`u|_|@`J~vU*N;(_6)dRqQ`T+0v7pxXFhJmcBaJ+l zPv)JKZkh+l=`0-susWl*4Pa@b0l31ug*Hu+e4bi$ajR?vtQzbiRZ?(h+Uo`q(c|dC2LYu5yGW-5g=&g zuQ^1%w0{yQhoD$c&`rqM21l`Tw%Z!h_wqfB-e?w7>vc<;@KI6hY0iD@EV%rqTPBaas~fC jZ_Oq-GIJf?{Fclg7tleBV~-ed00000NkvXXu0mjfCFFe5 literal 2468 zcmV;V30wAwP)DK~!jgy_!jL8`lxXe>0dF>;xnL+$8QGNQZf!C=G=!okW}9H}8!e|QhGBdp z2!c72VztiVi3Crc%=~1^mtt=}$>ZtodAYY=-gN)l$#4E%`l6x%3rkDPFD#-PHFwvK zikzzQ?D=!n*Eh$;h(zaq66FEB9*_CFpG?-2U0PZuo=B9wk`Tnt9zA;GF%4LW zt+KvxYPrq@NviH6Jf0TwIhj1D+t>WUBD*hNmcHuCtLh&mfc@k?%PT8qRr43fzt@x(eiySt`bGb>QUTsXq58`rVfYyb!bp9l}W{K|MVJxwmB*7>|P z6h&bu6vFMi$o8(CbV??l7m*|#mn(lmzK%r^8kp(8GXIS>*4mT zn{>3d1F*eouc~|zx-PJ@W8+e1=b6SVBA?H*7LNlpn7A55L13V-musV=Sgls`eL5vK z^nEHNpYT9uMLeD$pU>Bhh05V{I(Tbn2&dC|sx6;yY%a9Ch$xE4vW%*p{p{s6jjgTi z#tpdC*-3wYAEHosn@Ez5s@6BcA{Dl*rfH21T@{;KTlEdFS*;8W4dV59YsxuYD*JoZ znlb=xw@N0HIkPJ*4?$`g(AL^YaG<{?qZSeH9dVFUY93@!H1PSdtR=Rqw2MfRL|#>^ z29o6jSWz=ck_ZKZ1OooLiWea(I$d2UR#z*#Qc*PMx|~AO(!}E@;u_jo19WtB(&BcR z1eH8EB(a`gdwYjcPgx#}w=*^}f@E=!Sg*{evQl~Zolcd3fh0RSHc}~>eBOd2 zX}H~ae7-COS)M(cHTS>OYGrsRgs8ii{whr@xsq(IT#!uzO3=Z=XiPoYQ*CQX)bkkRBSiTfZc9GmgS}xwHDSlHdt6(MALLcQKY}G@01d;=y5}1 z#iCi>|3zhoVj3XJ1%;Yn)Jn+ZR3h_Hc6SSx54X$B@K6Yc!+uKkDBTzrWKxAmP-K~0 z8GD-s6r;Ol8MPF}X++3vCwbgaPMrU9AE5qV8( zicyQuG>xU@74zO|v)LF9h3W=v-9alqJkHb|t2bmNovzH@rU8awu(`QKt6y%IQK3LM z;c(b=j~(r84D=6RvD9D1n-fl=&yH#yWW_3QW5UJecC4~4AH4sb8C$HxVi;uU>PjJs zl{+mmswCxET3O-A)2AF|vnaC6=#?vk1_v8b9-!rdg?snC$V%nOQmg{^e(gn4j!7ie zE6NB{4=0U{%}v6QC;)D^i?NXrz*?12RrQeBNQBg33IK=0!PU_bjx!R|U#E#hkINX< zMauIQ@((G&MftJO+hZw~># zA5nZgn>ke_o=C8^7B}~!BuU)5ah|Vd~+eG4 z^1d&H^yU8^RBhBWt!C0m5Cq!VT6y?I{N3Z}GiSpCe!m%}h{52%nK~(A zCANyyk#C4mo$f;QXXe=pj%)b#TCi9w1PA)f-@??xNn?I~f%tl5sxC*R%8JY7WNc)F z+>xa!qYBayb8}(Rnfk$7EewT1v=^(G0^djg{R90Z4-Xsiy_NF%sZ^TB-#?+Zr-%3M z`LUGv`s3qc))Ffu66@zmt3$!-w7)LFsyh5mf@RTrNi>8fAWA z0jJZ6WDz;e6_QwQ0xfR$iLHacAMV__Gi`byMNzmqI%3|;s-a$GMb~u>4-eVfPrfnb zoel?Mqa#=>MK^5x_w9Gz{Tq}Ox*ZM&*RPH-^St^Yt5r?>G2nK)7#$fY%?|#cfm!T6 z#RCq9gB#bb;&eKGNy1Qs=Z9S325Cr}u z%F4&% diff --git a/pkg/ctr/assets/snes9x2002_banner.png b/pkg/ctr/assets/snes9x2002_banner.png index ddab3b446a3d2546cf83d81b56cbf1d6eb54c1dc..98b42d2175bd089cb6458f3733ea7c47b03a384f 100644 GIT binary patch literal 11277 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 IxBc_ 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 diff --git a/pkg/ctr/assets/snes9x2005.png b/pkg/ctr/assets/snes9x2005.png index a13439df65026d8fd97798daa841b80d68f7be62..e8efbc121b609ba4f586e24bc0b3c3f0513714dd 100644 GIT binary patch delta 699 zcmV;s0!01a6vzdT8Gi!+003az3AF$K09sH?R7J_g$E&KVkB*RviHozavdPBAM{rEG zy1kW@m7JTL!oR@A#>S(eqsYa`%E!mY#l_6b%u$TS1ZBmNw7XHUlLVu2(7~|OYiim$ zH`^^L&YYa-)N=aUM*rP7=LR$-0000HbW%=J|NsC0|EmE1|9}7gl>c-|*%?0O3|;^L z0t`t+K~#7Fjh5+_s~`}BOK@ypvM(-K-v2dk1Sv4+c)xS!^s!>%UQg55E-b^d|mT`$=H+e^BAWq1jm904`S?PiyC&EZYI~>{P#iESMqy z#D8brr<0K-j-fp z642!qx#QWhV^1{-#Xssr!Hdf$cHaVvcY?7eWOIjF&DWz~@eakq zD7-VN+814-nb`)A;;Cw1v3^%RW96nPX)z0L@ezQT%^Swfg=R0^ffHgjZ%l}Nj`33w zRB+y`V4R=SND9uI)%xS9Y1k|qScLi@*dFawtTd;9EwF?U);BmDel|X5jJNqG)HnG2 h_J|U*R_+eX#6RjZJTS2fkL~~f002ovPDHLkV1j4NSK0so literal 2655 zcmV-l3ZV6gP)7s^xtsi-ZS_9=RVFk_go57lh<@=K-YC*@i>e3?xCt$Ezx5` zmSuWQoxg1!t%<> zp)y_2uS|XX@i>4`yotl}5#p<58h<6QTCF^FX$*i&Cd1Tc*K5iLdV4t0*;!n)Xp+rO z7&EZ6eE+eNm!#@8qS@^>p0|_9x@|=k?y|ADSzL5FuPARxg$A~{x3qL*qQ&sEx6#qj ziMz>#D2nL1PBNJ!8jJH_W5bYhyPJ&XWOApWwu7NC7oRwf)oKMm(0N@_6ot89=-9+% zS*E|QkBsEzooSd{(b?SBXtlw_$tEt3xbb>72!_IFn)b*u0Fck;35G(P8y!Uu1OQ2R z_v*X)>T2RxF{?B>$N3&u!X zou(@J1EdwWI<1#llSf8ApJydnF!fbEaW#m7K=0{OoEsj-VzC(IlPir4#N>Vb&;~v# zqR|-ne7<%pR0@Z~&R~B(4u_+xZY$GR8=LzeiXvvS8AVa*e^3Q^Rb_2$y>SIjbav9y za~iR*`^(U36G-gUpM_R?;a64FM!T+x#M)Z@C1kZ&I5#>>ptq+gd4TZ~jdeP4QXrj9 zqiM$v&3ATpYtMks>m|_JQ`4g!;N?;H@NQwFR)a%^%cBB`_4SJNq9`Is5_v_b8c3Fg zAQhQOl0;u1fXCBbm-qoXT>@90EttwmN;qV=@^yh`Q-(-1cBszh_0ZALNt4TY;GjtE z>=KK|SYLlYv860^fMp8UY&J%QhS-ww{A%qwlj%5{YKB&ml?g`&mzxLZG&?IQ@ze={ z_r5J~by{cgzRpGl+N?0qC2)CEpjB3wnVB=zzr|u2{1ZH7_g7S+ z8g_U0mi1VGd5wh8`Z*;4by;ol+?(=hJcCIi;m&S}(XzH`; zhZQ(|svF7Q!uOXys7St8iTAo*z%SYB64!N|`_U*%OUr1QhGa4k2n6tXJ%|t1aliG8 zisXxx*!lTeOefY31%n4LtdJlGbol*T-3rzu50KXKT-}z7l zW-g6g#OL(@VExD6Ymx^LG8wENyw8cwj*9h$0ydi!v)RmKI$oC;z+|enTzqwv>o;zY z+({yeB7xpsMuvwE9n|I9(+%}yo|&epsj~MrHnG`k24G8V%%yHtk;~;2!jXA4HVTIi zm(#^Se;;hG{H?*3^V}_>@%<>O<3+ui(9nC6rF@lUG&7ZI#zrDJC3WMwF_m5?Q#* z?b%rrMZs#da(;A_)7{-w$phquMjGlXKRik*RoQzR3h25{BC*EHEd$ua+Md@Tnz`IE z2oxO7=Qq9}8Y?)b4!@7F3l|!SI5W?`fNm|H$ztd>8~e|{NMgP6x#dtSw6wfTyDoFZ zKT%Z)hiK-C{~3HH8;eUz+`M&*{Y(bgY-afES^CbL!DMPIO4r&w?7jMWRV8$rjlCcJ zn3T;%ELPcQ2vd{S^y0LJ%jINbXow9#;p)~blc_ixYKAtGl?g{DmsXYPZNknn(}|&l`mni-psty79EPBZ^;+W=>Iv#$v3jM2+<*NfH;IIFHZ0 zM8N%fOhT@#6IHhmNxnidJ5W`LN=PPm2!=vrYWNE2K(H9uY?fds%zR`4r_+HXNn~?5 zW5kQmb@C)`N2)4$0F#g<;Qj-*H-1W1ZK+FK*L9Yc?{jZ?xvuQPyinz|hN`MHb2fq? z;PZLuaQw9@`2(1QEFF%IxVQ7&nsVuMnz>Mzy}k0mDurw|BmMh7|E()n4K|w%*=(lS zQa`K!XqIcsMPo7UJ`6h=fyd){v@>xj|m6&U|FShy)r%YfB4Gr{mGnD#b{p(kP0;c6P8K`DE^_ z@vN#U;rR%2!O$a3)v(IlFIUDY2 zZ#O)>7zp$psgVOLE#GG+?Iewu^6#f{Mfl5 zzt2a1-fh(eEf!Cg=AS~ba==(nN>r*n-xvd*xlV_YdiVa$UE$IMuvxqW>)|3$)}(G z9h8LAcDtSPqa%m1&ZRiY&B}4$ayc0t9VyNZ&Z=Nsb_@|AqWE9-CYa~4ip=N6-^YrSxg>k z{8yro3hAMx+0FdI-6PxWM}^DfqAw7@=`6m3(C?Tu?ZvTYzE&1rOQ65>@cAVWzEdjD z*49RIb2BT^7%S1}k=%YGuv)Ejcb&xN^&UzD2m*f=&GKtwV`CLJoFS>}x-fNZ>J0(? zmq0~Rq^c^hSe(^F;<5b+#NFhg!{33|>nX`#2-||7|K!Q>@juo2{(l}jCDz;@4DA2_ N002ovPDHLkV1ktTFIfNp diff --git a/pkg/ctr/assets/snes9x2005_banner.png b/pkg/ctr/assets/snes9x2005_banner.png index 1167f68256c218ad2cddfa409cdb4c43a37c60e0..79dfbcc6cb89516f23cf2a01c6b93345616aa989 100644 GIT binary patch literal 10651 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 TfB3}!_oAkxtyrUA754uDtM=T+ literal 37857 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 XeMvlUDTny}00000NkvXXu0mjf0HbyC literal 2832 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 literal 47005 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

    cJ4W`c?nz51;wWmmPmR{)zF;Lqy#@_kLxUij-y1 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 literal 2877 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 b(EI-fA7(}c+pk=f00000NkvXXu0mjf#dUYT diff --git a/pkg/ctr/assets/snes9x2010_banner.png b/pkg/ctr/assets/snes9x2010_banner.png index 848afe190878cf17e33abe901b72f3573d58d713..cc5c6df41c8b8f8ec06844efa2f3d7c50dd83c97 100644 GIT binary patch literal 10709 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 z zR*`p-xRf-1(Yu{I*8iM-zWdTi9Ak2Pc6xd~wKl_Nu!hUh&E(q!*sG;=lKZ^}yb;g> zukwx3Yzj$j{!ZhF43TwS)hM4#PyWr>HYp9|$jQa)8JJ>%>2ydp!Uw@nN%%mN)f4+bTesA^8N7p-88w6dig)M2yt_hzN*v<{ANQa6ZsjH626 zBh_H|@QqTZ<|Hix#lVQdI9_fcrE|1gpLs?{=s@*I@ngO0KiZ8Fio*xyho0j})M6?l zd8t^bxMhP+=E;=g1v}M1Q~@4Y(Ewq^^RECFRe&MF9$RwIEi4UFMJ^(Rmvmr(RejYj z0-_wuo0ABZzPzX@ZD=iP^`>|woRl(C&)GsSn{?|~aoGRhfiKz2?<-PgqKAkC6jMl= zE?yOaG%~;x6kBN-1;>AAUW%}{XLh*bd&$xsCfI`{DQ>8e+ZBv(+%mnouvW$NPmQa6 z9vqUzHju;xEkI6%Q@`*4-1KmKDFYbzvh#~PrjdvJa8u*llsTcX!Hi4MsUH)oB&dSg{<#^r@Ry&Mr|gaCnP z=wiJy;fp-Sc{%6SkFoab5;(px^e&t4QYPb5k8qUdii=>B>D+$B-yv(@`OY6|2}#h9Rl|HwRaAoU^Tu*`@gfVri-F2FNE_fZFB6yL%Kv zV?z!FFA#^C3l@=wk**~12E%vpaCvb!j^wRATU2!M+f>|f7T-)a7;zMy+W^srK~k7v zm-Xcq{>d#oD9Eh1f)EcOHPu&}Uo^lt~L&IosR}&7((>_k=l8V?j{;Wrxlzy{vEbbu*Ns8tcfE z9?O+p-c~s5fedG!KxrD)>Zo!( z-vTmGk+*vBc1T)hcOE=0n-?3Gld+&z0B)AnRr2<6wueo?Ofh=m074Otti6<3YTl@vH;!%ojw%WxOUsZkqOX6};w`>1GlqzctIRn^y$90(w2FsYl8$>76Y(5Wf1Msvs20RnSP)D2$KvQ%0qER_rY>MJgN`E;(mubY zdQPlO*1p;ynvp+Le}|Jkwr7Z1yg{F1J@mks-g6Ojcl3#TV4KIbVPRZSViiqYXjhVx zM>9r7pl`5IvdU~+-z(kN-^To^g}wb};C2>btCl;*J9w&-w3VcSC&9}A&2py=8aCL( zBrko5+6fAHqo)}iR zzda=xC97JqSm&9oC9~bI5mo^#N1lBPbtJV2P_=o5Qe%EP%L~Xk z9g7@bo{}^LQ(87(gmN`P>u#Ki_qC<6*3dtHSl#_$z|w9ZeYJ2~j(r?%nEg7keAMz) z_Z(j|Wu@%qdj>y>izD1>+~H3~mJ3Z!KZDe?LqnbpWEX``&-mUV=h$1tS25vLa-mTV zILTFN#{X7-#IC^|Hg|VsskUb2U>gt!i6-VVkZV#o|4Ti$DpV7RN0Sh2<`om^Fd~Y& z!&MYict;i2RMsq&(_uA*CuQh~F0uK=R#OHYQA^+)8!aY`X8cLbG!<8~o8$gygRjov zI}hlFn_-G1s?g|%=j!FtimxF56e9R7;>!|~<@yG7wy(joG~xx(@!VRdg% z0LjAV>Jc{xYLR({)_?hG-I%7zm#;g!BSCgx_Ql#dRy&UF;a}Tz>Ts968v9r+x4ynk zMdvr9Nm{d8XC>+YeWW~wYVt{+C{13U4NujtZ|kSTo%hR(>}}VJjz+(%GC>&f>0tytKz#wgj&(XdL2OBx zlhy&Z*Zn(xB`?K=fl@#+humLSVe(_dg&W%O{?dy<=X@~sxAA=AZcG12i}Y+<9elD} zP#H1>#Z6gh+x(E)y&JcX@K7_z6k!Z00q+4Ol-8sV>7}(LTw+{~C>ZZXWDVyNT<`Ly SY3{F|0jf{56f5K{L;nxuRpT!J literal 34032 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 z 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%0DCq0cZ{U+kgAgC;&j( zEeIg(7&ITEhy-@*0Kzas1Lbl?;T3`;O+_U2`o+t@7H2}#rKKQ}xh&2M%06h30Fgsfe1%R!M0DwYB z0AN!?0H6>KSh3rAqmY+v1F$XMkCIpN{Jw6tcs#G@olfzw>l##@U((yG)Ri^)7^dI1 zr~E+C0DSO+I&T1QUAX`N*JT9k_AnX%HW~sTQ)r6}tb8ZRpBs6!E!*<4i&f^WWY`D{ zKQ<7*sPuqYhR;5>rS&|$_vKXn-~3GtLQpgSTcH5p+D72$QJ~Y=+6Vv{8URo*1OQTF zklzOs6@Az+TuZjm!M1!aWiu~7ELN#Ixyft!zso~=>+S7h^LJUEAP@P0>sNl@n%*D4 z1_Mw;0l>G91c0nM0AN!?U=abZ(HHaY{{R4B3k&T50j>oF`~U#=008p<0P+9;Gz|^(0Rc=h3%+p#|CRuTQV5=32I`6d zF(M6NJqoR81#?IV=#rH%0004iNkl>aRFnuB4#4G1pjigC1u=p{-ZMl?TUQqcM$=xD2(DHO_MkZSwx@8Qh$~g zZ{?XHfwP{nDlAo{W;py=U4ZKBBN6aRFMrdvUDvkFi@*#!pe&5qUep#wCCthS$V~lB z*T2=7kSco<7=Q4bkNAan&We$ckzVb3?5aW}q@u_a)Pqb>5d$F=#TcLwF#{n>PFz63 zKnRdx%i8vhf%ppG17Lqi2zZ)CfP74DzB#ZMOpGr={f8HwDoc99~-5+vH zzE4!`!=UGS`Ur>)y8$HP?3odbhkvrZ2e)XdJ%)+-48S(Bo!tON>@rIq5a|4Mxo$ut%Qf@)2>bwL<~b-}1`P86 P0000C01*)p92^`N7#JrfCp8WZzyt*I000vc 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 BGi(3= literal 16250 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 delta 614 zcmV-s0-62P1OEh&B!9qAOjJbx0095~{sIC5Dk>@i0|N;O35<)29UUDHPcqg@lAD zDJgb#cG}t53JMCqzrVk}zS-E=goA@TJUn4wU`R+v^z-wQl7EtScX!Xu&ZwuSVP9YU z{QUFs^6~KSMMXsc0Rbf?B`qy2tg5OpF){!D{{TFIsi>&i+S*i9RB>@}`T6;Eb#-80 zUznDb|AH`B0004cNklZ-n(3T?KRLC#H_jz#vH+i%JP*i=oO!?=|j(gnG25-%bGmqf|&_Z3^+m;?JQ`yFnRL4r*$r9 znIK`v5oR#5a7@|q=5x==g&-3Q{CX+4R}lY_DCs}e-?bp=>SYZPi7!sv8nkGQvgUgb zG~fO0O(^b07*qoM6N<$f=9X} A#sB~S diff --git a/pkg/ctr/assets/vecx_banner.png b/pkg/ctr/assets/vecx_banner.png index 90641977711f30781faefae5ccaac491a4928969..ad120c78ebfb932e9f308f99d1c01820c8444abb 100644 GIT binary patch literal 7043 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 BE9d|K delta 1245 zcmV<31S0$Y1n>!vBYy#>P)t-s0002LzrVk~zrVk~zrVk~zrVk~zrVk~zrVk~zrVk~ zzrVk~zrVk~zrVk~zrVk~zrVkbkdVK>zrn%5z`($+uCBhmzPr1-xVX5ludlhexrm5} zx3{;ftgMcXjt+JNNMZ6zP_QMp>&^|k&%(;y)3D>w!FN&r?j)g#Kg?R zr|HOB@X1P)pwFG1om!KS_sK4EiQSEx)a$=2f~BL2n$o|(!^OqLv$VIMprG%>W@g$Sy>b-n z>r_pj7k|HeCz32z>A^;f3@o{)c=np`+$hjNe=wJLcEdBeEq5ZtJgV+mD8 z*3^|1DH3Fh)-e31~E(M8T1NOBh$d@DdU%5uiXdV_L>Bj66~# z0K|$Qe=>$f$_ZH;!`Xip;(awjvLH~+STkQ?sH%-&6hP)l&{v0>nb&l$Bzfb4A><{6 zjekLRcd>v`fz(P^LB}$@5{!WYrf~)UXD|_Icx+^3EKJMLl-80+%f+&030I9SYg}YrSOtP-- zhnSFtyw6u7X|B-A(i6+M+!UNIV<^;ozJEbt7b4C3qoe!H5fE;IFq{L1LAX=ZTZ#sW zy`{Z_S&f4|6sB^y<%uK{D%_#Je4W+l=*N~OlgJO52{d6|E7Cq|(vCyxtb}n7RK~^W zyvFSB|JX5Ozt$^M7sR-oPA2@kwYhm6&QAN{C<=f>_{apvKYRseGn9PXg-?T8f`8Ch z;RSpjE5P!4$H%{iDGJUKID?HD>&;pMme^XK>zP~MI($nm0$qHQNij0-7vudM)*f7i z4=ID&j!nA>I~h~RKv$6k_LdeRaSQ4co~0}vSt)m+jS1b|-K}t3;|anc;TIDypX%Wj z;8nmpw}L=VUrKfmMOmKT<8DfL$%M^|USR zFk9rusUqPFJVb%v{B!3$Udj=;V7`SOMNlL^J&K^jRCfFikAW?VtKM7Z00000NkvXX Hu0mjfqN0u9 diff --git a/pkg/ctr/assets/yabause_banner.png b/pkg/ctr/assets/yabause_banner.png index 0dfc9675a4c23ee4943e7a0e073ca32289c4ad4b..ed72ed8cd629b218c9480cf31127ce291573d844 100644 GIT binary patch literal 7190 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 F{{teVMHm18 literal 17642 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 Date: Fri, 13 Apr 2018 01:29:15 +0100 Subject: [PATCH 291/517] icon changes --- pkg/ctr/Makefile.cores | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/ctr/Makefile.cores b/pkg/ctr/Makefile.cores index ec73cf9b5d..5fad8c802d 100644 --- a/pkg/ctr/Makefile.cores +++ b/pkg/ctr/Makefile.cores @@ -76,8 +76,8 @@ else ifeq ($(LIBRETRO), freeintv) APP_AUTHOR = various APP_PRODUCT_CODE = RARCH-FREEINTV APP_UNIQUE_ID = 0xBAC1G - APP_ICON = pkg/ctr/assets/default.png - APP_BANNER = pkg/ctr/assets/libretro_banner.png + APP_ICON = pkg/ctr/assets/freeintv.png + APP_BANNER = pkg/ctr/assets/freeintv_banner.png else ifeq ($(LIBRETRO), fuse) APP_TITLE = Fuse @@ -273,7 +273,7 @@ else ifeq ($(LIBRETRO), stella) APP_UNIQUE_ID = 0xBAC2C APP_ICON = pkg/ctr/assets/stella.png APP_BANNER = pkg/ctr/assets/stella_banner.png - + else ifeq ($(LIBRETRO), pokemini) APP_TITLE = PokeMini APP_AUTHOR = justburn @@ -282,4 +282,4 @@ else ifeq ($(LIBRETRO), pokemini) APP_ICON = pkg/ctr/assets/default.png APP_BANNER = pkg/ctr/assets/libretro_banner.png -endif \ No newline at end of file +endif From 6f9c2da807b938c4fb09f57cd0205d60346dbac4 Mon Sep 17 00:00:00 2001 From: Maxim Biro Date: Fri, 13 Apr 2018 03:35:33 -0400 Subject: [PATCH 292/517] CXXLAGS -> CXXFLAGS C++ definitely doesn't lag! --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f51031259d..e93d97ea95 100644 --- a/Makefile +++ b/Makefile @@ -133,7 +133,7 @@ RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ)) ifneq ($(X86),) CFLAGS += -m32 - CXXLAGS += -m32 + CXXFLAGS += -m32 LDFLAGS += -m32 endif From adf4ed960e9200710340c2f9db713b6cdaea1cd9 Mon Sep 17 00:00:00 2001 From: David Walters Date: Fri, 13 Apr 2018 10:31:21 +0100 Subject: [PATCH 293/517] Handle null and empty string calls to retro_opendir with a consistent null return. --- libretro-common/file/retro_dirent.c | 14 ++++++++------ libretro-common/include/retro_dirent.h | 10 ++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/libretro-common/file/retro_dirent.c b/libretro-common/file/retro_dirent.c index 648739a93c..dc3fa5dde0 100644 --- a/libretro-common/file/retro_dirent.c +++ b/libretro-common/file/retro_dirent.c @@ -106,14 +106,16 @@ struct RDIR *retro_opendir(const char *name) wchar_t *path_wide = NULL; unsigned path_len; #endif - struct RDIR *rdir = (struct RDIR*)calloc(1, sizeof(*rdir)); + struct RDIR *rdir; - if (!rdir||!name) - { - if (rdir) - free(rdir); + /*Reject null or empty string paths*/ + if (!name||(*name==0)) + return NULL; + + /*Allocate RDIR struct. Tidied later with retro_closedir*/ + rdir = (struct RDIR*)calloc(1, sizeof(*rdir)); + if (!rdir) return NULL; - } #if defined(_WIN32) (void)path_wide; diff --git a/libretro-common/include/retro_dirent.h b/libretro-common/include/retro_dirent.h index 090076ec64..51b7eac700 100644 --- a/libretro-common/include/retro_dirent.h +++ b/libretro-common/include/retro_dirent.h @@ -32,6 +32,16 @@ RETRO_BEGIN_DECLS typedef struct RDIR RDIR; +/** + * + * retro_opendir: + * @name : path to the directory to open. + * + * Opens a directory for reading. Tidy up with retro_closedir. + * + * Returns: RDIR pointer on success, NULL if name is not a + * valid directory, null itself or the empty string. + */ struct RDIR *retro_opendir(const char *name); int retro_readdir(struct RDIR *rdir); From 6f7b8aac8cacc1f11694286368af18a66938c08c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 14 Apr 2018 03:25:58 +0200 Subject: [PATCH 294/517] Add input/input_mapper.c always --- Makefile.common | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile.common b/Makefile.common index b12ea01505..3743a0c805 100644 --- a/Makefile.common +++ b/Makefile.common @@ -199,6 +199,7 @@ OBJ += frontend/frontend.o \ audio/audio_driver.o \ $(LIBRETRO_COMM_DIR)/audio/audio_mixer.o \ input/input_driver.o \ + input/input_mapper.o \ led/led_driver.o \ led/drivers/led_null.o \ gfx/video_coord_array.o \ @@ -1605,8 +1606,6 @@ ifeq ($(HAVE_NETWORKING), 1) $(LIBRETRO_COMM_DIR)/utils/md5.o endif - OBJ += input/input_mapper.o - ifeq ($(HAVE_NETWORKGAMEPAD), 1) OBJ += input/input_remote.o \ cores/libretro-net-retropad/net_retropad_core.o From dca36ebaf8101865bcaf4496d23d2d21a15df616 Mon Sep 17 00:00:00 2001 From: gblues Date: Sat, 14 Apr 2018 01:26:26 -0700 Subject: [PATCH 295/517] Add small snippet for atomic value swapping Fortunately, the gcc port implements the builtins and, from basic testing, they seem to work. This is only really useful on Wii U--other platforms have more robust atomic operations, or aren't using gcc to build. --- Makefile.wiiu | 4 +- input/common/hid/device_null.c | 211 +++++++++++++++++++++++++++++++++ wiiu/include/wiiu/os/atomic.h | 14 +++ wiiu/system/atomic.c | 36 ++++++ 4 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 input/common/hid/device_null.c create mode 100644 wiiu/include/wiiu/os/atomic.h create mode 100644 wiiu/system/atomic.c diff --git a/Makefile.wiiu b/Makefile.wiiu index 798028e5de..823233215e 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -32,6 +32,7 @@ OBJ += wiiu/input/wpad_driver.o OBJ += wiiu/input/kpad_driver.o OBJ += wiiu/input/pad_functions.o OBJ += wiiu/system/memory.o +OBJ += wiiu/system/atomic.o OBJ += wiiu/system/exception_handler.o OBJ += wiiu/fs/sd_fat_devoptab.o OBJ += wiiu/fs/fs_utils.o @@ -60,7 +61,8 @@ ifeq ($(WIIU_HID),1) input/common/hid/hid_device_driver.o \ input/common/hid/device_wiiu_gca.o \ input/common/hid/device_ds3.o \ - input/common/hid/device_ds4.o + input/common/hid/device_ds4.o \ + input/common/hid/device_null.o endif ifeq ($(SALAMANDER_BUILD),1) diff --git a/input/common/hid/device_null.c b/input/common/hid/device_null.c new file mode 100644 index 0000000000..18628859f5 --- /dev/null +++ b/input/common/hid/device_null.c @@ -0,0 +1,211 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "hid_device_driver.h" + +extern pad_connection_interface_t null_pad_connection; + +/* + * This is the instance data structure for the pad you are implementing. + * This is a good starting point, but you can add/remove things as makes + * sense for the pad you're writing for. The pointer to this structure + * will be passed in as a void pointer to the methods you implement below. + */ +typedef struct null_instance { + void *handle; /* a handle to the HID subsystem adapter */ + joypad_connection_t *pad; /* a pointer to the joypad connection you assign + in init() */ + int slot; /* which slot does this pad occupy? */ + uint32_t buttons; /* a bitmap of the digital buttons for the pad */ + uint16_t motors[2]; /* rumble strength, if appropriate */ + uint8_t data[64]; /* a buffer large enough to hold the device's + max rx packet */ +} null_instance_t; + +/** + * Use the HID_ macros (see input/include/hid_driver.h) to send data packets + * to the device. When this method returns, the device needs to be in a state + * where we can read data packets from the device. So, if there's any + * activation packets (see the ds3 and Wii U GameCube adapter drivers for + * examples), send them here. + * + * While you *can* allocate the retro pad here, it isn't mandatory (see + * the Wii U GC adapter). + * + * If initialization fails, return NULL. + */ +static void *null_init(void *handle) +{ + null_instance_t *instance; + instance = (null_instance_t *)calloc(1, sizeof(null_instance_t)); + if(!instance) + goto error; + + memset(instance, 0, sizeof(null_instance_t)); + instance->handle = handle; + instance->pad = hid_pad_register(instance, &null_pad_connection); + if(!instance->pad) + goto error; + + RARCH_LOG("[null]: init complete.\n"); + return instance; + + error: + RARCH_ERR("[null]: init failed.\n"); + if(instance) + free(instance); + + return NULL; +} + +/* + * Gets called when the pad is disconnected. It must clean up any memory + * allocated and used by the instance data. + */ +static void null_free(void *data) +{ + null_instance_t *instance = (null_instance_t *)data; + + if(instance) + free(instance); +} + +/** + * Handle a single packet from the device. + * For most pads you'd just forward it onto the pad driver (see below). + * A more complicated example is in the Wii U GC adapter driver. + */ +static void null_handle_packet(void *data, uint8_t *buffer, size_t size) +{ + null_instance_t *instance = (null_instance_t *)data; + + if(instance && instance->pad) + instance->pad->iface->packet_handler(instance->pad->data, buffer, size); +} + +/** + * Return true if the passed in VID and PID are supported by the driver. + */ +static bool null_detect(uint16_t vendor_id, uint16_t product_id) +{ + return vendor_id == VID_NONE && product_id == PID_NONE; +} + +/** + * Assign function pointers to the driver structure. + */ +hid_device_t null_hid_device = { + null_init, + null_free, + null_handle_packet, + null_detect, + "Null HID device" +}; + +/** + * This is called via hid_pad_register(). In the common case where the + * device only controls one pad, you can simply return the data parameter. + * But if you need to track multiple pads attached to the same HID device + * (see: Wii U GC adapter), you can allocate that memory here. + */ +static void *null_pad_init(void *data, uint32_t slot, hid_driver_t *driver) +{ + null_instance_t *instance = (null_instance_t *)data; + + if(!instance) + return NULL; + + instance->slot = slot; + return instance; +} + +/** + * If you allocate any memory in null_pad_init() above, de-allocate it here. + */ +static void null_pad_deinit(void *data) +{ +} + +/** + * Translate the button data from the pad into the retro_bits_t format + * that RetroArch can use. + */ +static void null_get_buttons(void *data, retro_bits_t *state) +{ + null_instance_t *instance = (null_instance_t *)data; + if(!instance) + return; + + /* TODO: get buttons */ +} + +/** + * Handle a single packet for the pad. + */ +static void null_packet_handler(void *data, uint8_t *packet, uint16_t size) +{ + null_instance_t *instance = (null_instance_t *)data; + if(!instance) + return; + + RARCH_LOG_BUFFER(packet, size); +} + +/** + * If the pad doesn't support rumble, then this can just be a no-op. + */ +static void null_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) +{ +} + +/** + * Read analog sticks. If the pad doesn't have any analog axis, just return 0 here. + */ +static int16_t null_get_axis(void *data, unsigned axis) +{ + return 0; +} + +/** + * The name the pad will show up as in the UI, also used to auto-assign + * buttons in input/input_autodetect_builtin.c + */ +static const char *null_get_name(void *data) +{ + return "Null HID Pad"; +} + +/** + * Read the state of a single button. + */ +static bool null_button(void *data, uint16_t joykey) +{ + return false; +} + +/** + * Fill in the joypad interface + */ +pad_connection_interface_t null_pad_connection = { + null_pad_init, + null_pad_deinit, + null_packet_handler, + null_set_rumble, + null_get_buttons, + null_get_axis, + null_get_name, + null_button +}; diff --git a/wiiu/include/wiiu/os/atomic.h b/wiiu/include/wiiu/os/atomic.h new file mode 100644 index 0000000000..a5e5dfbcf4 --- /dev/null +++ b/wiiu/include/wiiu/os/atomic.h @@ -0,0 +1,14 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t SwapAtomic8(uint8_t *ptr, uint8_t value); +uint16_t SwapAtomic16(uint16_t *ptr, uint16_t value); +uint32_t SwapAtomic32(uint32_t *ptr, uint32_t value); + +#ifdef __cplusplus +} +#endif diff --git a/wiiu/system/atomic.c b/wiiu/system/atomic.c new file mode 100644 index 0000000000..ec16f75876 --- /dev/null +++ b/wiiu/system/atomic.c @@ -0,0 +1,36 @@ +/* devkitPPC is missing a few functions that are kinda needed for some cores. + * This should add them back in. + */ + +#include + +/** + * Performs an atomic swap of a single piece of data. In each function, + * value is written to ptr, and the previous value is returned. + * + * From GCC docs: + * + * "This builtin is not a full barrier, but rather a *release* barrier. This + * means that references after the builtin cannot move to (or be speculated + * to) before the builtin, but previous memory stores may not be globally + * visible yet, and previous memory loads may not yet be satisfied." + * + * https://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Atomic-Builtins.html + * + * This builtin seems to be implemented in the Wii U GCC toolchain. But since + * this is GCC-specific, I'm not going to put it into a more general-use + * location. + */ +uint8_t SwapAtomic8(uint8_t *ptr, uint8_t value) +{ + return __sync_lock_test_and_set(ptr, value); +} +uint16_t SwapAtomic16(uint16_t *ptr, uint16_t value) +{ + return __sync_lock_test_and_set(ptr, value); +} +uint32_t SwapAtomic32(uint32_t *ptr, uint32_t value) +{ + return __sync_lock_test_and_set(ptr, value); +} + From b0a2df41dd9796d38219ea99cbaa702468c9164b Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 14 Apr 2018 14:08:28 -0400 Subject: [PATCH 296/517] libretro-db: Ignore compiled binaries This makes it so that the compiled libretro-db binaries are ignored from git. --- libretro-db/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 libretro-db/.gitignore diff --git a/libretro-db/.gitignore b/libretro-db/.gitignore new file mode 100644 index 0000000000..09ef21d176 --- /dev/null +++ b/libretro-db/.gitignore @@ -0,0 +1,4 @@ +# Ignore compiled binaries. +/c_converter +/libretrodb_tool +/rmsgpack_test From d3bbdb2e068073584c9e2ca1fe950ae1fb796a44 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 14 Apr 2018 14:09:46 -0400 Subject: [PATCH 297/517] libretro-db: Show missing key match warning once Before, the "missing match" warning would appear for every entry. This change makes it show that it only displays the warning once. --- libretro-db/c_converter.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/libretro-db/c_converter.c b/libretro-db/c_converter.c index f08171ca17..564c43fa53 100644 --- a/libretro-db/c_converter.c +++ b/libretro-db/c_converter.c @@ -494,6 +494,7 @@ static dat_converter_list_t* dat_converter_parser( dat_converter_map_t map; dat_converter_list_item_t* current = lexer_list->values; bool skip = true; + bool warning_displayed = false; map.key = NULL; map.type = DAT_CONVERTER_LIST_MAP; @@ -531,13 +532,16 @@ static dat_converter_list_t* dat_converter_parser( // If the key is not found, report, and mark it to be skipped. if (!map.key) { - printf("Missing match key '"); - while (match_key->next) - { - printf("%s.", match_key->value); - match_key = match_key->next; + if (warning_displayed == false) { + printf(" - Missing match key '"); + while (match_key->next) + { + printf("%s.", match_key->value); + match_key = match_key->next; + } + printf("%s' on line %d\n", match_key->value, current->token.line_no); + warning_displayed = true; } - printf("%s' on line %d\n", match_key->value, current->token.line_no); skip = true; } } From d44f9467e42c97fb98804535c195ff58b7fa75d1 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 14 Apr 2018 14:10:40 -0400 Subject: [PATCH 298/517] libretro-db: Fix whitespace --- libretro-db/c_converter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libretro-db/c_converter.c b/libretro-db/c_converter.c index 564c43fa53..3d2e2ac615 100644 --- a/libretro-db/c_converter.c +++ b/libretro-db/c_converter.c @@ -532,7 +532,8 @@ static dat_converter_list_t* dat_converter_parser( // If the key is not found, report, and mark it to be skipped. if (!map.key) { - if (warning_displayed == false) { + if (warning_displayed == false) + { printf(" - Missing match key '"); while (match_key->next) { From 9d352af3a82f62289431b6fb93ea84c8d63fd342 Mon Sep 17 00:00:00 2001 From: "Steven M. Vascellaro" Date: Sat, 14 Apr 2018 15:17:40 -0400 Subject: [PATCH 299/517] retroarch.cfg: Rename "UI" to "User Interface" Use full name for UI to match the terminology when changing settings in RGUI. --- retroarch.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/retroarch.cfg b/retroarch.cfg index 5517a67458..019c14c326 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -720,7 +720,7 @@ video_message_bgcolor_opacity = 1.0 # Check for firmware requirement(s) before loading a content. # check_firmware_before_loading = "false" -#### UI +#### User Interface # Suspends the screensaver if set to true. Is a hint that does not necessarily have to be honored # by video driver. From 97e09d179f53472e89b28382324dfe5000088c52 Mon Sep 17 00:00:00 2001 From: gblues Date: Sat, 14 Apr 2018 13:30:34 -0700 Subject: [PATCH 300/517] Fix deadlocks when device is unplugged == DETAILS TIL that it's bad to call synchronization code from callbacks. To avoid that, I made the following changes: - Implemented an atomic swap (see previous commit) to avoid explicit locking when working with the event list - ensure locks are only acquired in either the main thread or the I/O polling thread - use an explicit polling loop; we still use async reads, but the read doesn't immediately re-invoke itself. - remove the sleep in the polling thread. - remove unnecessary locking in the thread cleanup call--verified that the list can't be modified while it is being executed. == TESTING I tested locally, and was able to disconnect/reconnect USB devices several times without the worker thread getting deadlocked. --- input/common/hid/device_ds3.c | 6 +- input/common/hid/device_ds4.c | 117 ++++++++++++++++-- input/common/hid/hid_device_driver.c | 2 +- wiiu/input/wiiu_hid.c | 172 ++++++++++++++++----------- wiiu/input/wiiu_hid.h | 1 + 5 files changed, 216 insertions(+), 82 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index 0bda535d41..e06b0a6c6f 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -153,14 +153,16 @@ static void *ds3_init(void *handle) if(!instance) goto error; + memset(instance, 0, sizeof(ds3_instance_t)); instance->handle = handle; -/* maybe not necessary? */ -/* RARCH_LOG("[ds3]: setting protocol\n"); +/* if(set_protocol(instance, 1)) errors++; */ + set_protocol(instance, 1); + RARCH_LOG("[ds3]: sending control packet\n"); if(send_control_packet(instance) < 0) errors++; diff --git a/input/common/hid/device_ds4.c b/input/common/hid/device_ds4.c index 861de2751f..ce17221b35 100644 --- a/input/common/hid/device_ds4.c +++ b/input/common/hid/device_ds4.c @@ -16,27 +16,63 @@ #include "hid_device_driver.h" -struct ds4_instance { - hid_driver_t *driver; - void *handle; -}; +extern pad_connection_interface_t ds4_pad_connection; +typedef struct ds4_instance { + void *handle; + joypad_connection_t *pad; + int slot; + uint32_t buttons; + uint16_t motors[2]; + uint8_t data[64]; +} ds4_instance_t; + +/** + * I'm leaving this code in here for posterity, and because maybe it can + * be used on other platforms. But using the DS4 on the Wii U directly is + * impossible because it doesn't generate a HID event. Which makes me think + * it's not a HID device at all--at least, not over USB. + * + * I imagine it might be useful in Bluetooth mode, though. + */ static void *ds4_init(void *handle) { - return NULL; + ds4_instance_t *instance; + instance = (ds4_instance_t *)calloc(1, sizeof(ds4_instance_t)); + if(!instance) + goto error; + + memset(instance, 0, sizeof(ds4_instance_t)); + instance->handle = handle; + instance->pad = hid_pad_register(instance, &ds4_pad_connection); + if(!instance->pad) + goto error; + + RARCH_LOG("[ds4]: init complete.\n"); + return instance; + + error: + RARCH_ERR("[ds4]: init failed.\n"); + if(instance) + free(instance); + + return NULL; } static void ds4_free(void *data) { - struct ds4_instance *instance = (struct ds4_instance *)data; - if(!instance) - return; + ds4_instance_t *instance = (ds4_instance_t *)data; - free(instance); + if(instance) + free(instance); } static void ds4_handle_packet(void *data, uint8_t *buffer, size_t size) { + ds4_instance_t *instance = (ds4_instance_t *)data; + + if(instance && instance->pad) + instance->pad->iface->packet_handler(instance->pad->data, buffer, size); } static bool ds4_detect(uint16_t vendor_id, uint16_t product_id) @@ -51,3 +87,66 @@ hid_device_t ds4_hid_device = { ds4_detect, "Sony DualShock 4" }; + +static void *ds4_pad_init(void *data, uint32_t slot, hid_driver_t *driver) +{ + ds4_instance_t *instance = (ds4_instance_t *)data; + + if(!instance) + return NULL; + + instance->slot = slot; + return instance; +} + +static void ds4_pad_deinit(void *data) +{ +} + +static void ds4_get_buttons(void *data, retro_bits_t *state) +{ + ds4_instance_t *instance = (ds4_instance_t *)data; + if(!instance) + return; + + /* TODO: get buttons */ +} + +static void ds4_packet_handler(void *data, uint8_t *packet, uint16_t size) +{ + ds4_instance_t *instance = (ds4_instance_t *)data; + if(!instance) + return; + + RARCH_LOG_BUFFER(packet, size); +} + +static void ds4_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) +{ +} + +static int16_t ds4_get_axis(void *data, unsigned axis) +{ + return 0; +} + +static const char *ds4_get_name(void *data) +{ + return "Sony DualShock 4"; +} + +static bool ds4_button(void *data, uint16_t joykey) +{ + return false; +} + +pad_connection_interface_t ds4_pad_connection = { + ds4_pad_init, + ds4_pad_deinit, + ds4_packet_handler, + ds4_set_rumble, + ds4_get_buttons, + ds4_get_axis, + ds4_get_name, + ds4_button +}; diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c index 37bc4feaa8..b38bdd9067 100644 --- a/input/common/hid/hid_device_driver.c +++ b/input/common/hid/hid_device_driver.c @@ -21,7 +21,7 @@ hid_driver_instance_t hid_instance = {0}; hid_device_t *hid_device_list[] = { &wiiu_gca_hid_device, &ds3_hid_device, - &ds4_hid_device, +/* &ds4_hid_device, */ NULL /* must be last entry in list */ }; diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index 63c9317430..8990d9ce44 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -15,6 +15,7 @@ */ #include "wiiu_hid.h" +#include static wiiu_event_list events; static wiiu_adapter_list adapters; @@ -377,21 +378,17 @@ static void synchronized_process_adapters(wiiu_hid_t *hid) static void synchronized_add_event(wiiu_attach_event *event) { - OSFastMutex_Lock(&(events.lock)); - event->next = events.list; - events.list = event; - OSFastMutex_Unlock(&(events.lock)); + wiiu_attach_event *head = (wiiu_attach_event *)SwapAtomic32((uint32_t *)&events.list, 0); + + event->next = head; + head = event; + + SwapAtomic32((uint32_t *)&events.list, (uint32_t)head); } static wiiu_attach_event *synchronized_get_events_list(void) { - wiiu_attach_event *list; - OSFastMutex_Lock(&(events.lock)); - list = events.list; - events.list = NULL; - OSFastMutex_Unlock(&(events.lock)); - - return list; + return (wiiu_attach_event *)SwapAtomic32((uint32_t *)&events.list, 0); } static wiiu_adapter_t *synchronized_lookup_adapter(uint32_t handle) @@ -422,24 +419,24 @@ static int32_t wiiu_attach_callback(HIDClient *client, { wiiu_attach_event *event = NULL; - switch(attach) - { - case HID_DEVICE_ATTACH: - log_device(device); - case HID_DEVICE_DETACH: - if (device) - event = new_attach_event(device); - - if(!event) - goto error; - - event->type = attach; - synchronized_add_event(event); - return DEVICE_USED; - default: - break; + if(attach) { + RARCH_LOG("[hid]: Device attach event generated.\n"); + log_device(device); + } else { + RARCH_LOG("[hid]: Device detach event generated.\n"); } + if (device) + event = new_attach_event(device); + + if(!event) + goto error; + + event->type = attach; + synchronized_add_event(event); + + return DEVICE_USED; + error: delete_attach_event(event); return DEVICE_UNUSED; @@ -453,7 +450,7 @@ static void wiiu_hid_detach(wiiu_hid_t *hid, wiiu_attach_event *event) * the read loop method will update this to ADAPTER_STATE_GC * so the adapter poll method can clean it up. */ if(adapter) - adapter->state = ADAPTER_STATE_DONE; + adapter->connected = false; } @@ -479,15 +476,6 @@ error: delete_adapter(adapter); } -void wiiu_start_read_loop(wiiu_adapter_t *adapter) -{ - HIDRead(adapter->handle, - adapter->rx_buffer, - adapter->rx_size, - wiiu_hid_read_loop_callback, - adapter); -} - static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, uint8_t *buffer, uint32_t buffer_size, void *userdata) { @@ -498,17 +486,49 @@ static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, return; } - if(adapter->hid->polling_thread_quit) + if(error < 0) { - RARCH_LOG("Shutting down read loop for device: %s\n", - adapter->driver->name); - adapter->state = ADAPTER_STATE_DONE; + int16_t hid_error_code = error & 0xffff; + switch(hid_error_code) + { + case -100: + RARCH_ERR("[hid]: Invalid RM command (%s)\n", adapter->driver->name); + break; + case -102: + RARCH_ERR("[hid]: Invalid IOCTL command (%s)\n", adapter->driver->name); + break; + case -103: + RARCH_ERR("[hid]: bad vector count (%s)\n", adapter->driver->name); + break; + case -104: + RARCH_ERR("[hid]: invalid memory bank (%s)\n", adapter->driver->name); + break; + case -105: + RARCH_ERR("[hid]: invalid memory alignment (%s)\n", adapter->driver->name); + break; + case -106: + RARCH_ERR("[hid]: invalid data size (%s)\n", adapter->driver->name); + break; + case -107: + RARCH_ERR("[hid]: request cancelled (%s)\n", adapter->driver->name); + break; + case -108: + RARCH_ERR("[hid]: request timed out (%s)\n", adapter->driver->name); + break; + case -109: + RARCH_ERR("[hid]: request aborted (%s)\n", adapter->driver->name); + break; + case -110: + RARCH_ERR("[hid]: client priority error (%s)\n", adapter->driver->name); + break; + case -111: + RARCH_ERR("[hid]: invalid device handle (%s)\n", adapter->driver->name); + break; + } } - if(adapter->state == ADAPTER_STATE_READY || - adapter->state == ADAPTER_STATE_READING) { - - adapter->state = ADAPTER_STATE_READING; + if(adapter->state == ADAPTER_STATE_READING) { + adapter->state = ADAPTER_STATE_READY; /* "error" usually is something benign like "device not ready", at * least from my own experiments. Just ignore the error and retry * the read. */ @@ -517,16 +537,6 @@ static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, buffer, buffer_size); } } - - /* this can also get set if something goes wrong in initialization */ - if(adapter->state == ADAPTER_STATE_DONE) - { - adapter->state = ADAPTER_STATE_GC; - return; - } - - HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, - wiiu_hid_read_loop_callback, adapter); } /** @@ -540,17 +550,18 @@ static void wiiu_hid_polling_thread_cleanup(OSThread *thread, void *stack) RARCH_LOG("Waiting for in-flight reads to finish.\n"); + /* We don't need to protect the adapter list here because nothing else + will access it during this method (the HID system is shut down, and + the only other access is the polling thread that just stopped */ do { - OSFastMutex_Lock(&(adapters.lock)); incomplete = 0; for(adapter = adapters.list; adapter != NULL; adapter = adapter->next) { if(adapter->state == ADAPTER_STATE_READING) incomplete++; } - /* We are clear for shutdown. Clean up the list - * while we are holding the lock. */ + if(incomplete == 0) { RARCH_LOG("All in-flight reads complete.\n"); @@ -562,7 +573,6 @@ static void wiiu_hid_polling_thread_cleanup(OSThread *thread, void *stack) delete_adapter(adapter); } } - OSFastMutex_Unlock(&(adapters.lock)); if(incomplete) usleep(5000); @@ -572,8 +582,7 @@ static void wiiu_hid_polling_thread_cleanup(OSThread *thread, void *stack) RARCH_WARN("[hid]: timed out waiting for in-flight read to finish.\n"); incomplete = 0; } - }while(incomplete); - + } while(incomplete); } static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list) @@ -594,7 +603,7 @@ static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list) } } -static void wiiu_handle_ready_adapters(wiiu_hid_t *hid) +static void wiiu_poll_adapters(wiiu_hid_t *hid) { wiiu_adapter_t *it; OSFastMutex_Lock(&(adapters.lock)); @@ -602,11 +611,25 @@ static void wiiu_handle_ready_adapters(wiiu_hid_t *hid) for(it = adapters.list; it != NULL; it = it->next) { if(it->state == ADAPTER_STATE_READY) - wiiu_start_read_loop(it); + { + if(it->connected) + { + it->state = ADAPTER_STATE_READING; + HIDRead(it->handle, + it->rx_buffer, + it->rx_size, + wiiu_hid_read_loop_callback, + it); + } else { + it->state = ADAPTER_STATE_DONE; + } + } + + if(it->state == ADAPTER_STATE_DONE) + it->state = ADAPTER_STATE_GC; } OSFastMutex_Unlock(&(adapters.lock)); - } static int wiiu_hid_polling_thread(int argc, const char **argv) @@ -618,8 +641,7 @@ static int wiiu_hid_polling_thread(int argc, const char **argv) while(!hid->polling_thread_quit) { wiiu_handle_attach_events(hid, synchronized_get_events_list()); - wiiu_handle_ready_adapters(hid); - usleep(10000); + wiiu_poll_adapters(hid); } RARCH_LOG("[hid]: polling thread is stopping\n"); @@ -674,6 +696,17 @@ static void delete_hidclient(HIDClient *client) free(client); } +static void init_cachealigned_buffer(int32_t min_size, uint8_t **out_buf_ptr, int32_t *actual_size) +{ + int cacheblocks = (min_size < 32) ? 1 : min_size / 32; + if(min_size > 32 && min_size % 32 != 0) + cacheblocks++; + + *actual_size = 32 * cacheblocks; + + *out_buf_ptr = alloc_zeroed(32, *actual_size); +} + static wiiu_adapter_t *new_adapter(wiiu_attach_event *event) { wiiu_adapter_t *adapter = alloc_zeroed(32, sizeof(wiiu_adapter_t)); @@ -683,10 +716,9 @@ static wiiu_adapter_t *new_adapter(wiiu_attach_event *event) adapter->handle = event->handle; adapter->interface_index = event->interface_index; - adapter->rx_size = event->max_packet_size_rx; - adapter->rx_buffer = alloc_zeroed(32, adapter->rx_size); - adapter->tx_size = event->max_packet_size_tx; - adapter->tx_buffer = alloc_zeroed(32, adapter->tx_size); + init_cachealigned_buffer(event->max_packet_size_rx, &adapter->rx_buffer, &adapter->rx_size); + init_cachealigned_buffer(event->max_packet_size_tx, &adapter->tx_buffer, &adapter->tx_size); + adapter->connected = true; return adapter; } diff --git a/wiiu/input/wiiu_hid.h b/wiiu/input/wiiu_hid.h index 2a060d8dd2..0c96c9599c 100644 --- a/wiiu/input/wiiu_hid.h +++ b/wiiu/input/wiiu_hid.h @@ -62,6 +62,7 @@ struct wiiu_adapter { int32_t tx_size; uint32_t handle; uint8_t interface_index; + bool connected; }; /** From 6ab91a422eef83ee014e14bc9511e92b4acba02e Mon Sep 17 00:00:00 2001 From: gblues Date: Sat, 14 Apr 2018 14:34:13 -0700 Subject: [PATCH 301/517] Small cleanup to adapt to upstream code changes retro_bits_t turned into input_bits_t and there were parts of my code that needed to update. == TESTING No idea if upstream changes broke anything, but it compiles cleanly now. --- input/common/hid/device_ds3.c | 2 +- input/common/hid/device_ds4.c | 2 +- input/common/hid/device_null.c | 4 ++-- input/common/hid/device_wiiu_gca.c | 2 +- input/include/hid_driver.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index e06b0a6c6f..365d945b2e 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -236,7 +236,7 @@ static void ds3_pad_deinit(void *data) ds3_instance_t *pad = (ds3_instance_t *)data; } -static void ds3_get_buttons(void *data, retro_bits_t *state) +static void ds3_get_buttons(void *data, input_bits_t *state) { ds3_instance_t *pad = (ds3_instance_t *)data; diff --git a/input/common/hid/device_ds4.c b/input/common/hid/device_ds4.c index ce17221b35..b334a13233 100644 --- a/input/common/hid/device_ds4.c +++ b/input/common/hid/device_ds4.c @@ -103,7 +103,7 @@ static void ds4_pad_deinit(void *data) { } -static void ds4_get_buttons(void *data, retro_bits_t *state) +static void ds4_get_buttons(void *data, input_bits_t *state) { ds4_instance_t *instance = (ds4_instance_t *)data; if(!instance) diff --git a/input/common/hid/device_null.c b/input/common/hid/device_null.c index 18628859f5..96d99f5f74 100644 --- a/input/common/hid/device_null.c +++ b/input/common/hid/device_null.c @@ -140,10 +140,10 @@ static void null_pad_deinit(void *data) } /** - * Translate the button data from the pad into the retro_bits_t format + * Translate the button data from the pad into the input_bits_t format * that RetroArch can use. */ -static void null_get_buttons(void *data, retro_bits_t *state) +static void null_get_buttons(void *data, input_bits_t *state) { null_instance_t *instance = (null_instance_t *)data; if(!instance) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 1d50c513ee..5e3968f757 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -229,7 +229,7 @@ static void wiiu_gca_pad_deinit(void *data) } } -static void wiiu_gca_get_buttons(void *data, retro_bits_t *state) +static void wiiu_gca_get_buttons(void *data, input_bits_t *state) { gca_pad_t *pad = (gca_pad_t *)data; if(pad) diff --git a/input/include/hid_driver.h b/input/include/hid_driver.h index 5b75c785bb..02b6529f63 100644 --- a/input/include/hid_driver.h +++ b/input/include/hid_driver.h @@ -39,7 +39,7 @@ struct hid_driver bool (*query_pad)(void *handle, unsigned pad); void (*free)(const void *handle); bool (*button)(void *handle, unsigned pad, uint16_t button); - void (*get_buttons)(void *handle, unsigned pad, retro_bits_t *state); + void (*get_buttons)(void *handle, unsigned pad, input_bits_t *state); int16_t (*axis)(void *handle, unsigned pad, uint32_t axis); void (*poll)(void *handle); bool (*set_rumble)(void *handle, unsigned pad, enum retro_rumble_effect effect, uint16_t); From 4cd301bd929c765b968ce45b95bd749cce4b442d Mon Sep 17 00:00:00 2001 From: gblues Date: Sat, 14 Apr 2018 21:30:44 -0700 Subject: [PATCH 302/517] Add pad unregistration == DETAILS I think this will fix the problem with duplicate pads--pads weren't properly de-initializing and registering as disconnected. When a pad is disconnected, the slot should properly release now. --- input/common/hid/device_ds3.c | 4 +++- input/common/hid/device_ds4.c | 4 +++- input/common/hid/device_null.c | 4 +++- input/common/hid/device_wiiu_gca.c | 34 ++-------------------------- input/common/hid/hid_device_driver.c | 14 ++++++++++++ input/common/hid/hid_device_driver.h | 1 + 6 files changed, 26 insertions(+), 35 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index 365d945b2e..bd4d6a1ad4 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -192,8 +192,10 @@ static void ds3_free(void *data) { ds3_instance_t *instance = (ds3_instance_t *)data; - if(instance) + if(instance) { + hid_pad_deregister(instance->pad); free(instance); + } } static void ds3_handle_packet(void *data, uint8_t *packet, size_t size) diff --git a/input/common/hid/device_ds4.c b/input/common/hid/device_ds4.c index b334a13233..af923cee13 100644 --- a/input/common/hid/device_ds4.c +++ b/input/common/hid/device_ds4.c @@ -63,8 +63,10 @@ static void ds4_free(void *data) { ds4_instance_t *instance = (ds4_instance_t *)data; - if(instance) + if(instance) { + hid_pad_deregister(instance->pad); free(instance); + } } static void ds4_handle_packet(void *data, uint8_t *buffer, size_t size) diff --git a/input/common/hid/device_null.c b/input/common/hid/device_null.c index 96d99f5f74..783c615924 100644 --- a/input/common/hid/device_null.c +++ b/input/common/hid/device_null.c @@ -79,8 +79,10 @@ static void null_free(void *data) { null_instance_t *instance = (null_instance_t *)data; - if(instance) + if(instance) { + hid_pad_deregister(instance->pad); free(instance); + } } /** diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 5e3968f757..9e09598022 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -145,34 +145,6 @@ static void update_pad_state(wiiu_gca_instance_t *instance) } } -/* -static joypad_connection_t *register_pad(wiiu_gca_instance_t *instance, int port) { - int slot; - joypad_connection_t *result; - - if(!instance || !instance->online) - { - RARCH_ERR("[gca]: bad instance\n"); - return NULL; - } - - slot = pad_connection_find_vacant_pad(hid_instance.pad_list); - if(slot < 0) - { - RARCH_ERR("[gca]: failed to find a free slot\n"); - return NULL; - } - - result = &(hid_instance.pad_list[slot]); - result->iface = &wiiu_gca_pad_connection; - result->data = result->iface->init(instance, slot, hid_instance.os_driver); - result->connected = true; - input_pad_connect(slot, hid_instance.pad_driver); - - return result; -} -*/ - static void unregister_pad(wiiu_gca_instance_t *instance, int slot) { if(!instance || slot < 0 || slot >= 4 || instance->pads[slot] == NULL) @@ -180,10 +152,8 @@ static void unregister_pad(wiiu_gca_instance_t *instance, int slot) joypad_connection_t *pad = instance->pads[slot]; instance->pads[slot] = NULL; - pad->iface->deinit(pad->data); - pad->data = NULL; - pad->iface = NULL; - pad->connected = false; + + hid_pad_deregister(pad); } static bool wiiu_gca_detect(uint16_t vendor_id, uint16_t product_id) { diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c index b38bdd9067..ccab2dc569 100644 --- a/input/common/hid/hid_device_driver.c +++ b/input/common/hid/hid_device_driver.c @@ -60,6 +60,20 @@ joypad_connection_t *hid_pad_register(void *pad_handle, pad_connection_interface return result; } +void hid_pad_deregister(joypad_connection_t *pad) +{ + if(!pad) + return; + + if(pad->data) { + pad->iface->deinit(pad->data); + pad->data = NULL; + } + + pad->iface = NULL; + pad->connected = false; +} + static bool init_pad_list(hid_driver_instance_t *instance, unsigned slots) { if(!instance || slots > MAX_USERS) diff --git a/input/common/hid/hid_device_driver.h b/input/common/hid/hid_device_driver.h index 715fd7cfea..18a8f45479 100644 --- a/input/common/hid/hid_device_driver.h +++ b/input/common/hid/hid_device_driver.h @@ -37,6 +37,7 @@ extern hid_driver_instance_t hid_instance; hid_device_t *hid_device_driver_lookup(uint16_t vendor_id, uint16_t product_id); joypad_connection_t *hid_pad_register(void *pad_handle, pad_connection_interface_t *iface); +void hid_pad_deregister(joypad_connection_t *pad); bool hid_init(hid_driver_instance_t *instance, hid_driver_t *hid_driver, input_device_driver_t *pad_driver, unsigned slots); void hid_deinit(hid_driver_instance_t *instance); From 6eebbe42133ce6ff9fd1c2c52a2fb077fe6d50e3 Mon Sep 17 00:00:00 2001 From: gblues Date: Sun, 15 Apr 2018 00:04:49 -0700 Subject: [PATCH 303/517] Build fix for PC == DETAILS Hooray for conditional compile directives. Moving things around broke things in unexpected ways on non-WiiU builds. Well, not *completely* unexpected. But still. Changes: - Move some typedefs around to avoid circular include dependencies - Include the file where the HID driver definition got moved to == TESTING - verified build for Wii U still runs successfully - did a local build without any errors (some weird warnings, but since they happen in code I didn't change, I'm assuming they're pre-existing?) --- input/connect/joypad_connection.h | 2 -- input/input_driver.h | 17 ++++------------- input/input_types.h | 5 +++++ 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/input/connect/joypad_connection.h b/input/connect/joypad_connection.h index bb1b12158a..62a454609a 100644 --- a/input/connect/joypad_connection.h +++ b/input/connect/joypad_connection.h @@ -64,8 +64,6 @@ typedef struct pad_connection_interface bool (*button)(void *data, uint16_t joykey); } pad_connection_interface_t; -typedef struct joypad_connection joypad_connection_t; - extern pad_connection_interface_t pad_connection_wii; extern pad_connection_interface_t pad_connection_wiiupro; extern pad_connection_interface_t pad_connection_ps3; diff --git a/input/input_driver.h b/input/input_driver.h index 1fe417bd78..f47e8830eb 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -38,23 +38,11 @@ #include "../msg_hash.h" #include "include/hid_types.h" +#include "include/hid_driver.h" #include "include/gamepad.h" RETRO_BEGIN_DECLS -typedef struct -{ - uint32_t data[8]; - uint16_t analogs[8]; -} input_bits_t; - -typedef struct rarch_joypad_driver input_device_driver_t; - -typedef struct hid_driver hid_driver_t; - -/* Keyboard line reader. Handles textual input in a direct fashion. */ -typedef struct input_keyboard_line input_keyboard_line_t; - enum input_device_type { INPUT_DEVICE_TYPE_NONE = 0, @@ -621,6 +609,9 @@ const char *input_joypad_name(const input_device_driver_t *driver, bool input_config_get_bind_idx(unsigned port, unsigned *joy_idx_real); #ifdef HAVE_HID + +#include "include/hid_driver.h" + /** * hid_driver_find_handle: * @index : index of driver to get handle to. diff --git a/input/input_types.h b/input/input_types.h index 4c36861866..0c1e2edcf1 100644 --- a/input/input_types.h +++ b/input/input_types.h @@ -22,5 +22,10 @@ typedef struct input_keyboard_line input_keyboard_line_t; typedef struct rarch_joypad_info rarch_joypad_info_t; typedef struct input_driver input_driver_t; typedef struct input_keyboard_ctx_wait input_keyboard_ctx_wait_t; +typedef struct { + uint32_t data[8]; + uint16_t analogs[8]; +} input_bits_t; +typedef struct joypad_connection joypad_connection_t; #endif /* __INPUT_TYPES__H */ From 8e1437ad86c47c1e98693773507112fb44c2d5e4 Mon Sep 17 00:00:00 2001 From: Ash Date: Sun, 15 Apr 2018 17:19:39 +1000 Subject: [PATCH 304/517] [WiiU] Add build information to exception handler --- wiiu/system/exception_handler.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wiiu/system/exception_handler.c b/wiiu/system/exception_handler.c index d6138fac00..2caa0779c7 100644 --- a/wiiu/system/exception_handler.c +++ b/wiiu/system/exception_handler.c @@ -22,6 +22,8 @@ #include #include "wiiu_dbg.h" #include "exception_handler.h" +#include "version.h" +#include "version_git.h" /* Settings */ #define NUM_STACK_TRACE_LINES 5 @@ -182,6 +184,11 @@ void __attribute__((__noreturn__)) exception_cb(OSContext* ctx, OSExceptionType else buf_add("Stack pointer invalid. Could not trace further.\n"); +#ifdef HAVE_GIT_VERSION + buf_add("RetroArch " PACKAGE_VERSION " (%s) built " __DATE__, retroarch_git_version); +#else + buf_add("RetroArch " PACKAGE_VERSION " built " __DATE__); +#endif OSFatal(exception_msgbuf); for (;;) {} } From 87cf910396c008035b752e3b9c14014f60bc57ff Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 17:12:28 +0200 Subject: [PATCH 305/517] (CRT switch) Cleanups --- gfx/video_crt_switch.c | 380 +++++++++++++++++++---------------------- gfx/video_crt_switch.h | 29 +++- gfx/video_driver.c | 60 +++---- gfx/video_driver.h | 6 - 4 files changed, 226 insertions(+), 249 deletions(-) diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index d9bded15f9..ef9d4b169b 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -24,271 +24,241 @@ #include #endif - #include "video_driver.h" #include "video_crt_switch.h" -static float ra_tmp_core_hz; -static int ra_core_width; -static int ra_core_height; -static int ra_tmp_width; -static int ra_tmp_height; -static float fly_aspect; -static float ra_core_hz; -static int ra_set_core_hz; -static int orig_width; -static int orig_height; -static int first_run; +static int ra_core_width = 0; +static int ra_core_height = 0; +static int ra_tmp_width = 0; +static int ra_tmp_height = 0; +static int ra_set_core_hz = 0; +static int orig_width = 0; +static int orig_height = 0; +static int first_run = 0; + +static float ra_tmp_core_hz = 0.0f; +static float fly_aspect = 0.0f; +static float ra_core_hz = 0.0f; void crt_switch_res_core(int width, int height, float hz) { - /* ben_core_hz float passed from with in void video_driver_monitor_adjust_system_rates(void) */ - ra_core_width = width; + /* ra_core_hz float passed from within + * void video_driver_monitor_adjust_system_rates(void) */ + ra_core_width = width; ra_core_height = height; - ra_core_hz = hz; + ra_core_hz = hz; + crt_check_first_run(); - - if (ra_tmp_height != ra_core_height || ra_core_width != ra_tmp_width) - { /* detect resolution change and switch */ + + /* Detect resolution change and switch */ + if ( + (ra_tmp_height != ra_core_height) || + (ra_core_width != ra_tmp_width) + ) crt_screen_setup_aspect(width,height); - } - ra_tmp_height = ra_core_height; - ra_tmp_width = ra_core_width; - + ra_tmp_height = ra_core_height; + ra_tmp_width = ra_core_width; + + /* Check if aspect is correct, if notchange */ if (video_driver_get_aspect_ratio() != fly_aspect) - { /* check aspect is correct else change */ + { video_driver_set_aspect_ratio_value((float)fly_aspect); - crt_poke_video(); + video_driver_apply_state_changes(); } } -void crt_check_first_run() -{ /* ruin of first boot to get current display resolution */ + +void crt_check_first_run(void) +{ if (first_run != 1) { - #if defined(_WIN32) - orig_height = GetSystemMetrics(SM_CYSCREEN); - orig_width = GetSystemMetrics(SM_CXSCREEN); - #endif - + /* Run of first boot to get current display resolution */ +#if defined(_WIN32) + orig_height = GetSystemMetrics(SM_CYSCREEN); + orig_width = GetSystemMetrics(SM_CXSCREEN); +#endif } + first_run = 1; } +/* Create correct aspect to fit video if resolution does not exist */ void crt_screen_setup_aspect(int width, int height) -{ /* create correct aspect to fit video if resolution does not exist */ +{ switch_crt_hz(); - /* get original resolution of core */ - - + + /* get original resolution of core */ if (height == 4) - { + { + /* detect menu only */ if (width < 1920) - { width = 640; - }/* detect menu only */ - height = 480; - crt_aspect_ratio_switch(width,height); - } - if (height < 191 && height != 144) - { - crt_aspect_ratio_switch(width,height); - height = 200; - } - if (height > 191) - { - crt_aspect_ratio_switch(width,height); - } - if (height == 144 && ra_set_core_hz == 50) - { - height = 288; - crt_aspect_ratio_switch(width,height); - } - if (height > 200 && height < 224) - { - crt_aspect_ratio_switch(width,height); - height = 224; - } - if (height > 224 && height < 240) - { - crt_aspect_ratio_switch(width,height); - height = 240; - } - if (height > 240 && height < 255 ) - { - crt_aspect_ratio_switch(width,height); - height = 254; - } - if (height == 528 && ra_set_core_hz == 60) - { - crt_aspect_ratio_switch(width,height); - height = 480; - } - if (height >= 240 && height < 255 && ra_set_core_hz == 55) - { - crt_aspect_ratio_switch(width,height); - height = 254; - } - + height = 480; + crt_aspect_ratio_switch(width,height); + } + + if (height < 191 && height != 144) + { + crt_aspect_ratio_switch(width,height); + height = 200; + } + + if (height > 191) + crt_aspect_ratio_switch(width,height); + + if (height == 144 && ra_set_core_hz == 50) + { + height = 288; + crt_aspect_ratio_switch(width,height); + } + + if (height > 200 && height < 224) + { + crt_aspect_ratio_switch(width,height); + height = 224; + } + + if (height > 224 && height < 240) + { + crt_aspect_ratio_switch(width,height); + height = 240; + } + + if (height > 240 && height < 255) + { + crt_aspect_ratio_switch(width,height); + height = 254; + } + + if (height == 528 && ra_set_core_hz == 60) + { + crt_aspect_ratio_switch(width,height); + height = 480; + } + + if (height >= 240 && height < 255 && ra_set_core_hz == 55) + { + crt_aspect_ratio_switch(width,height); + height = 254; + } + switch_res_crt(width, height); } -void switch_res_crt(int width, int height){ - - if ( height > 100) +void switch_res_crt(int width, int height) +{ + if (height > 100) { - crt_switch_res(width, height,0,ra_set_core_hz); - crt_poke_video(); + video_driver_apply_state_changes(); } } -void crt_aspect_ratio_switch(int width,int height) -{ /* send aspect float to videeo_driver */ - fly_aspect = (float)width/height; +void crt_aspect_ratio_switch(int width, int height) +{ + /* send aspect float to videeo_driver */ + fly_aspect = (float)width / height; video_driver_set_aspect_ratio_value((float)fly_aspect); } -void switch_crt_hz() -{ /* set hz float an int for windows switching */ - - if (ra_core_hz != ra_tmp_core_hz) - { - if (ra_core_hz < 53 ) - { +void switch_crt_hz(void) +{ + if (ra_core_hz == ra_tmp_core_hz) + return; + + /* set hz float an int for windows switching */ + if (ra_core_hz < 53) ra_set_core_hz = 50; - } if (ra_core_hz >= 53 && ra_core_hz < 57) - { ra_set_core_hz = 55; - } - if (ra_core_hz >= 57 ) - { + if (ra_core_hz >= 57) ra_set_core_hz = 60; - } video_monitor_set_refresh_rate(ra_set_core_hz); ra_tmp_core_hz = ra_core_hz; - } } -void crt_video_restore() +void crt_video_restore(void) { - crt_switch_res(orig_width, orig_height,0,60); - } void crt_switch_res(int width, int height, int f_restore,int ra_hz) -{ /* windows function to swith resolutions */ +{ + /* Windows function to switch resolutions */ - #if defined(_WIN32) - DEVMODE curDevmode; - DEVMODE devmode; - DWORD flags = 0; - - int iModeNum; - int depth = 0; - int freq; - LONG res; +#if defined(_WIN32) + LONG res; + DEVMODE curDevmode; + DEVMODE devmode; - if (f_restore == 0) - { - freq = ra_hz; - } - else - { - freq = 0; - } - - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); - - if (width == curDevmode.dmPelsWidth) - { - width = 0; /* used to stop superresolution bug */ - } - if (width == 0) - { - width = curDevmode.dmPelsWidth; - } - if (height == 0) - { - height = curDevmode.dmPelsHeight; - } - if (depth == 0) - { - depth = curDevmode.dmBitsPerPel; - } - if (freq == 0) - { - freq = curDevmode.dmDisplayFrequency; - } + int iModeNum; + int freq; + DWORD flags = 0; + int depth = 0; - for (iModeNum = 0; ; iModeNum++) + if (f_restore == 0) + freq = ra_hz; + else + freq = 0; + + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); + + /* used to stop superresolution bug */ + if (width == curDevmode.dmPelsWidth) + width = 0; + if (width == 0) + width = curDevmode.dmPelsWidth; + if (height == 0) + height = curDevmode.dmPelsHeight; + if (depth == 0) + depth = curDevmode.dmBitsPerPel; + if (freq == 0) + freq = curDevmode.dmDisplayFrequency; + + for (iModeNum = 0; ; iModeNum++) + { + if (!EnumDisplaySettings(NULL, iModeNum, &devmode)) + break; + + if (devmode.dmPelsWidth != width) + continue; + + if (devmode.dmPelsHeight != height) + continue; + + if (devmode.dmBitsPerPel != depth) + continue; + + if (devmode.dmDisplayFrequency != freq) + continue; + + devmode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; + res = ChangeDisplaySettings(&devmode, CDS_TEST); + + switch (res) { - if (EnumDisplaySettings(NULL, iModeNum, &devmode)) - { - - if (devmode.dmPelsWidth != width) - { - continue; - } - - if (devmode.dmPelsHeight != height) - { - continue; - } - - if (devmode.dmBitsPerPel != depth) - { - continue; - } - - if (devmode.dmDisplayFrequency != freq) - { - continue; - } - - devmode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; - res = ChangeDisplaySettings(&devmode, CDS_TEST); - + case DISP_CHANGE_SUCCESSFUL: + res = ChangeDisplaySettings(&devmode, flags); switch (res) { - case DISP_CHANGE_SUCCESSFUL: - res = ChangeDisplaySettings(&devmode, flags); - switch (res) - { - case DISP_CHANGE_SUCCESSFUL: - return; - - case DISP_CHANGE_NOTUPDATED: - return; - - default: - - break; + case DISP_CHANGE_SUCCESSFUL: + return; + case DISP_CHANGE_NOTUPDATED: + return; + default: + break; } - break; - - case DISP_CHANGE_RESTART: - - break; - - default: - - break; - } - } - else - { break; - } + case DISP_CHANGE_RESTART: + break; + default: + break; } - #endif + } +#elif defined(linux) - #if defined(linux) - - #endif +#endif } diff --git a/gfx/video_crt_switch.h b/gfx/video_crt_switch.h index c93d9dea0e..dffb34ad4e 100644 --- a/gfx/video_crt_switch.h +++ b/gfx/video_crt_switch.h @@ -17,11 +17,32 @@ * If not, see . */ -void crt_check_first_run(); +#ifndef __VIDEO_CRT_SWITCH_H__ +#define __VIDEO_CRT_SWITCH_H__ + +#include + +#include +#include + +RETRO_BEGIN_DECLS + +void crt_check_first_run(void); + void crt_switch_res_core(int width, int height, float hz); + void crt_screen_setup_aspect(int width, int height); + void switch_res_crt(int width, int height); + void crt_aspect_ratio_switch(int width,int height); -void switch_crt_hz(); -void crt_video_restore(); -void crt_switch_res(int width, int height, int f_restore,int ra_hz); + +void switch_crt_hz(void); + +void crt_video_restore(void); + +void crt_switch_res(int width, int height, int f_restore, int ra_hz); + +RETRO_END_DECLS + +#endif diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 5838962696..10bb453aa7 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -119,9 +119,6 @@ #define video_driver_context_unlock() ((void)0) #endif -static bool crt_switching_active; -static float video_driver_core_hz; - typedef struct video_pixel_scaler { @@ -146,10 +143,12 @@ static rarch_softfilter_t *video_driver_state_filter = NULL; static void *video_driver_state_buffer = NULL; static unsigned video_driver_state_scale = 0; static unsigned video_driver_state_out_bpp = 0; -static bool video_driver_state_out_rgb32 = false; +static bool video_driver_state_out_rgb32 = false; +static bool video_driver_crt_switching_active = false; static struct retro_system_av_info video_driver_av_info; + static enum retro_pixel_format video_driver_pix_fmt = RETRO_PIXEL_FORMAT_0RGB1555; static const void *frame_cache_data = NULL; @@ -158,6 +157,7 @@ static unsigned frame_cache_height = 0; static size_t frame_cache_pitch = 0; static bool video_driver_threaded = false; +static float video_driver_core_hz = 0.0f; static float video_driver_aspect_ratio = 0.0f; static unsigned video_driver_width = 0; static unsigned video_driver_height = 0; @@ -1425,14 +1425,11 @@ void video_driver_monitor_adjust_system_rates(void) if (!info || info->fps <= 0.0) return; - if (crt_switching_active == true) - { + if (video_driver_crt_switching_active) timing_skew = fabs(1.0f - info->fps / video_driver_core_hz); - } else - { timing_skew = fabs(1.0f - info->fps / video_refresh_rate); - } + /* We don't want to adjust pitch too much. If we have extreme cases, * just don't readjust at all. */ if (timing_skew <= settings->floats.audio_max_timing_skew) @@ -1442,15 +1439,17 @@ void video_driver_monitor_adjust_system_rates(void) video_refresh_rate, (float)info->fps); - if (crt_switching_active == true){ - if (info->fps <= video_driver_core_hz) - return; + if (video_driver_crt_switching_active) + { + if (info->fps <= video_driver_core_hz) + return; } else { - if (info->fps <= video_refresh_rate) - return; + if (info->fps <= video_refresh_rate) + return; } + /* We won't be able to do VSync reliably when game FPS > monitor FPS. */ rarch_ctl(RARCH_CTL_SET_NONBLOCK_FORCED, NULL); RARCH_LOG("[Video]: Game FPS > Monitor FPS. Cannot rely on VSync.\n"); @@ -2604,29 +2603,22 @@ void video_driver_frame(const void *data, unsigned width, runloop_msg_queue_push(video_info.fps_text, 2, 1, true); /* trigger set resolution*/ - if (video_info.crt_switch_resolution == true){ - crt_switching_active = true; + if (video_info.crt_switch_resolution) + { + video_driver_crt_switching_active = true; - if (video_info.crt_switch_resolution_super == 2560){ - width = 2560; - } - if (video_info.crt_switch_resolution_super == 3840){ - width = 3840; - } - if (video_info.crt_switch_resolution_super == 1920){ - width = 1920; - } - crt_switch_res_core(width, height, video_driver_core_hz); - } else if (video_info.crt_switch_resolution == false){ - crt_switching_active = false; - } + if (video_info.crt_switch_resolution_super == 2560) + width = 2560; + if (video_info.crt_switch_resolution_super == 3840) + width = 3840; + if (video_info.crt_switch_resolution_super == 1920) + width = 1920; + crt_switch_res_core(width, height, video_driver_core_hz); + } + else if (!video_info.crt_switch_resolution) + video_driver_crt_switching_active = false; /* trigger set resolution*/ - -} - -void crt_poke_video(){ - video_driver_poke->apply_state_changes(video_driver_data); } void video_driver_display_type_set(enum rarch_display_type type) diff --git a/gfx/video_driver.h b/gfx/video_driver.h index a0cf6be1d2..d1a2551986 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -71,12 +71,6 @@ RETRO_BEGIN_DECLS #define MAX_VARIABLES 64 -/* Added for resolution wswitching */ -void crt_poke_video(); - -/* Added for resolution wswitching */ - - enum { TEXTURES = 8, From 359de5c84b0ac3b85836ff5e7a65ace60c1364a0 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 17:55:37 +0200 Subject: [PATCH 306/517] Create win32_change_display_settings --- gfx/common/win32_common.c | 31 ++++++++++++++++--------------- gfx/common/win32_common.h | 3 +++ gfx/video_crt_switch.c | 11 +++++++---- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index 4373168740..fb46870faa 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -256,6 +256,19 @@ void win32_monitor_from_window(void) #endif } +int win32_change_display_settings(const char *str, void *devmode_data, + unsigned flags) +{ +#if _WIN32_WINDOWS >= 0x0410 || _WIN32_WINNT >= 0x0410 + /* Windows 98 and later codepath */ + return ChangeDisplaySettingsEx(str, (DEVMODE*)devmode_data, + NULL, flags, NULL); +#else + /* Windows 95 / NT codepath */ + return ChangeDisplaySettings((DEVMODE*)devmode_data, flags); +#endif +} + void win32_monitor_get_info(void) { MONITORINFOEX current_mon; @@ -265,13 +278,7 @@ void win32_monitor_get_info(void) GetMonitorInfo(win32_monitor_last, (LPMONITORINFO)¤t_mon); -#if _WIN32_WINDOWS >= 0x0410 || _WIN32_WINNT >= 0x0410 - /* Windows 98 and later codepath */ - ChangeDisplaySettingsEx(current_mon.szDevice, NULL, NULL, 0, NULL); -#else - /* Windows 95 / NT codepath */ - ChangeDisplaySettings(NULL, 0); -#endif + win32_change_display_settings(current_mon.szDevice, NULL, 0); } void win32_monitor_info(void *data, void *hm_data, unsigned *mon_id) @@ -902,14 +909,8 @@ static bool win32_monitor_set_fullscreen( RARCH_LOG("Setting fullscreen to %ux%u @ %uHz on device %s.\n", width, height, refresh, dev_name); -#if _WIN32_WINDOWS >= 0x0410 || _WIN32_WINNT >= 0x0410 - /* Windows 98 and later codepath */ - return ChangeDisplaySettingsEx(dev_name, &devmode, - NULL, CDS_FULLSCREEN, NULL) == DISP_CHANGE_SUCCESSFUL; -#else - /* Windows 95 / NT codepath */ - return ChangeDisplaySettings(&devmode, CDS_FULLSCREEN); -#endif + return win32_change_display_settings(dev_name, &devmode, + CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL; #endif } diff --git a/gfx/common/win32_common.h b/gfx/common/win32_common.h index d780dc5a67..4220997fc2 100644 --- a/gfx/common/win32_common.h +++ b/gfx/common/win32_common.h @@ -56,6 +56,9 @@ void win32_monitor_get_info(void); void win32_monitor_info(void *data, void *hm_data, unsigned *mon_id); +int win32_change_display_settings(const char *str, void *devmode_data, + unsigned flags); + void create_graphics_context(HWND hwnd, bool *quit); void create_gdi_context(HWND hwnd, bool *quit); diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index ef9d4b169b..12a1de932a 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -21,7 +21,8 @@ #include #if defined(_WIN32) - #include +#include +#include "common/win32_common.h" #endif #include "video_driver.h" @@ -235,13 +236,15 @@ void crt_switch_res(int width, int height, int f_restore,int ra_hz) if (devmode.dmDisplayFrequency != freq) continue; - devmode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; - res = ChangeDisplaySettings(&devmode, CDS_TEST); + devmode.dmFields |= + DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; + res = + win32_change_display_settings(NULL, &devmode, CDS_TEST); switch (res) { case DISP_CHANGE_SUCCESSFUL: - res = ChangeDisplaySettings(&devmode, flags); + res = win32_change_display_settings(NULL, &devmode, flags); switch (res) { case DISP_CHANGE_SUCCESSFUL: From dc7c4fea2ddbb307e159e87bbbc6732b11c44749 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 18:31:46 +0200 Subject: [PATCH 307/517] Prevent Coverity warning --- gfx/video_driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 10bb453aa7..bf5dd9b7b7 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -1420,11 +1420,12 @@ void video_driver_monitor_adjust_system_rates(void) const struct retro_system_timing *info = (const struct retro_system_timing*)&video_driver_av_info.timing; rarch_ctl(RARCH_CTL_UNSET_NONBLOCK_FORCED, NULL); - video_driver_core_hz = info->fps; if (!info || info->fps <= 0.0) return; + video_driver_core_hz = info->fps; + if (video_driver_crt_switching_active) timing_skew = fabs(1.0f - info->fps / video_driver_core_hz); else From bb05af0d92212e29b1e4d566c670129c21192dc1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 18:39:42 +0200 Subject: [PATCH 308/517] (libchdr) Prevent memory leak --- libretro-common/formats/libchdr/huffman.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libretro-common/formats/libchdr/huffman.c b/libretro-common/formats/libchdr/huffman.c index 04b0300dd0..dd041a76c9 100644 --- a/libretro-common/formats/libchdr/huffman.c +++ b/libretro-common/formats/libchdr/huffman.c @@ -294,7 +294,10 @@ enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, /* make sure we ended up with the right number */ if (curcode != decoder->numcodes) + { + delete_huffman_decoder(smallhuff); return HUFFERR_INVALID_DATA; + } /* assign canonical codes for all nodes based on their code lengths */ error = huffman_assign_canonical_codes(decoder); From 1c6a6c80f42257b2ff1ed18e05bef4dfc943b295 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 18:42:25 +0200 Subject: [PATCH 309/517] Try to prevent another memory leak - this time in miniupnpc --- deps/miniupnpc/minissdpc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/miniupnpc/minissdpc.c b/deps/miniupnpc/minissdpc.c index 2332780d10..2a526d04e8 100644 --- a/deps/miniupnpc/minissdpc.c +++ b/deps/miniupnpc/minissdpc.c @@ -606,6 +606,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[], { if(error) *error = MINISSDPC_SOCKET_ERROR; + closesocket(sudp); return NULL; } From 4d87772f4a5cfbf5de35e93270ed1289cf1c8e65 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 18:52:38 +0200 Subject: [PATCH 310/517] (video_crt_switch.c) Turn a whole bunch of functions static --- gfx/video_crt_switch.c | 269 ++++++++++++++++++++--------------------- gfx/video_crt_switch.h | 12 -- 2 files changed, 133 insertions(+), 148 deletions(-) diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index 12a1de932a..bd26298617 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -41,36 +41,7 @@ static float ra_tmp_core_hz = 0.0f; static float fly_aspect = 0.0f; static float ra_core_hz = 0.0f; -void crt_switch_res_core(int width, int height, float hz) -{ - /* ra_core_hz float passed from within - * void video_driver_monitor_adjust_system_rates(void) */ - ra_core_width = width; - ra_core_height = height; - ra_core_hz = hz; - - crt_check_first_run(); - - /* Detect resolution change and switch */ - if ( - (ra_tmp_height != ra_core_height) || - (ra_core_width != ra_tmp_width) - ) - crt_screen_setup_aspect(width,height); - - ra_tmp_height = ra_core_height; - ra_tmp_width = ra_core_width; - - /* Check if aspect is correct, if notchange */ - if (video_driver_get_aspect_ratio() != fly_aspect) - { - video_driver_set_aspect_ratio_value((float)fly_aspect); - video_driver_apply_state_changes(); - } -} - - -void crt_check_first_run(void) +static void crt_check_first_run(void) { if (first_run != 1) { @@ -84,109 +55,8 @@ void crt_check_first_run(void) first_run = 1; } -/* Create correct aspect to fit video if resolution does not exist */ - -void crt_screen_setup_aspect(int width, int height) -{ - switch_crt_hz(); - - /* get original resolution of core */ - if (height == 4) - { - /* detect menu only */ - if (width < 1920) - width = 640; - height = 480; - crt_aspect_ratio_switch(width,height); - } - - if (height < 191 && height != 144) - { - crt_aspect_ratio_switch(width,height); - height = 200; - } - - if (height > 191) - crt_aspect_ratio_switch(width,height); - - if (height == 144 && ra_set_core_hz == 50) - { - height = 288; - crt_aspect_ratio_switch(width,height); - } - - if (height > 200 && height < 224) - { - crt_aspect_ratio_switch(width,height); - height = 224; - } - - if (height > 224 && height < 240) - { - crt_aspect_ratio_switch(width,height); - height = 240; - } - - if (height > 240 && height < 255) - { - crt_aspect_ratio_switch(width,height); - height = 254; - } - - if (height == 528 && ra_set_core_hz == 60) - { - crt_aspect_ratio_switch(width,height); - height = 480; - } - - if (height >= 240 && height < 255 && ra_set_core_hz == 55) - { - crt_aspect_ratio_switch(width,height); - height = 254; - } - - switch_res_crt(width, height); -} - -void switch_res_crt(int width, int height) -{ - if (height > 100) - { - crt_switch_res(width, height,0,ra_set_core_hz); - video_driver_apply_state_changes(); - } -} - -void crt_aspect_ratio_switch(int width, int height) -{ - /* send aspect float to videeo_driver */ - fly_aspect = (float)width / height; - video_driver_set_aspect_ratio_value((float)fly_aspect); -} - -void switch_crt_hz(void) -{ - if (ra_core_hz == ra_tmp_core_hz) - return; - - /* set hz float an int for windows switching */ - if (ra_core_hz < 53) - ra_set_core_hz = 50; - if (ra_core_hz >= 53 && ra_core_hz < 57) - ra_set_core_hz = 55; - if (ra_core_hz >= 57) - ra_set_core_hz = 60; - video_monitor_set_refresh_rate(ra_set_core_hz); - ra_tmp_core_hz = ra_core_hz; -} - -void crt_video_restore(void) -{ - crt_switch_res(orig_width, orig_height,0,60); -} - - -void crt_switch_res(int width, int height, int f_restore,int ra_hz) +static void crt_switch_res(int width, int height, + int f_restore,int ra_hz) { /* Windows function to switch resolutions */ @@ -196,14 +66,12 @@ void crt_switch_res(int width, int height, int f_restore,int ra_hz) DEVMODE devmode; int iModeNum; - int freq; + int freq = 0; DWORD flags = 0; int depth = 0; if (f_restore == 0) freq = ra_hz; - else - freq = 0; EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); @@ -265,3 +133,132 @@ void crt_switch_res(int width, int height, int f_restore,int ra_hz) #endif } + +static void switch_crt_hz(void) +{ + if (ra_core_hz == ra_tmp_core_hz) + return; + + /* set hz float an int for windows switching */ + if (ra_core_hz < 53) + ra_set_core_hz = 50; + if (ra_core_hz >= 53 && ra_core_hz < 57) + ra_set_core_hz = 55; + if (ra_core_hz >= 57) + ra_set_core_hz = 60; + video_monitor_set_refresh_rate(ra_set_core_hz); + ra_tmp_core_hz = ra_core_hz; +} + +static void crt_aspect_ratio_switch(int width, int height) +{ + /* send aspect float to videeo_driver */ + fly_aspect = (float)width / height; + video_driver_set_aspect_ratio_value((float)fly_aspect); +} + +static void switch_res_crt(int width, int height) +{ + if (height > 100) + { + crt_switch_res(width, height,0,ra_set_core_hz); + video_driver_apply_state_changes(); + } +} + +/* Create correct aspect to fit video if resolution does not exist */ +static void crt_screen_setup_aspect(int width, int height) +{ + switch_crt_hz(); + + /* get original resolution of core */ + if (height == 4) + { + /* detect menu only */ + if (width < 1920) + width = 640; + height = 480; + crt_aspect_ratio_switch(width,height); + } + + if (height < 191 && height != 144) + { + crt_aspect_ratio_switch(width,height); + height = 200; + } + + if (height > 191) + crt_aspect_ratio_switch(width,height); + + if (height == 144 && ra_set_core_hz == 50) + { + height = 288; + crt_aspect_ratio_switch(width,height); + } + + if (height > 200 && height < 224) + { + crt_aspect_ratio_switch(width,height); + height = 224; + } + + if (height > 224 && height < 240) + { + crt_aspect_ratio_switch(width,height); + height = 240; + } + + if (height > 240 && height < 255) + { + crt_aspect_ratio_switch(width,height); + height = 254; + } + + if (height == 528 && ra_set_core_hz == 60) + { + crt_aspect_ratio_switch(width,height); + height = 480; + } + + if (height >= 240 && height < 255 && ra_set_core_hz == 55) + { + crt_aspect_ratio_switch(width,height); + height = 254; + } + + switch_res_crt(width, height); +} + + +void crt_switch_res_core(int width, int height, float hz) +{ + /* ra_core_hz float passed from within + * void video_driver_monitor_adjust_system_rates(void) */ + ra_core_width = width; + ra_core_height = height; + ra_core_hz = hz; + + crt_check_first_run(); + + /* Detect resolution change and switch */ + if ( + (ra_tmp_height != ra_core_height) || + (ra_core_width != ra_tmp_width) + ) + crt_screen_setup_aspect(width,height); + + ra_tmp_height = ra_core_height; + ra_tmp_width = ra_core_width; + + /* Check if aspect is correct, if notchange */ + if (video_driver_get_aspect_ratio() != fly_aspect) + { + video_driver_set_aspect_ratio_value((float)fly_aspect); + video_driver_apply_state_changes(); + } +} + +void crt_video_restore(void) +{ + crt_switch_res(orig_width, orig_height, 0, 60); +} diff --git a/gfx/video_crt_switch.h b/gfx/video_crt_switch.h index dffb34ad4e..3f3c2ccbed 100644 --- a/gfx/video_crt_switch.h +++ b/gfx/video_crt_switch.h @@ -27,22 +27,10 @@ RETRO_BEGIN_DECLS -void crt_check_first_run(void); - void crt_switch_res_core(int width, int height, float hz); -void crt_screen_setup_aspect(int width, int height); - -void switch_res_crt(int width, int height); - -void crt_aspect_ratio_switch(int width,int height); - -void switch_crt_hz(void); - void crt_video_restore(void); -void crt_switch_res(int width, int height, int f_restore, int ra_hz); - RETRO_END_DECLS #endif From 311c156e081e794de39e337ea3a2445331217b60 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 18:55:16 +0200 Subject: [PATCH 311/517] Turn width/height into unsigned --- gfx/video_crt_switch.c | 36 ++++++++++++++++++------------------ gfx/video_crt_switch.h | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index bd26298617..722f4f6c20 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -28,13 +28,13 @@ #include "video_driver.h" #include "video_crt_switch.h" -static int ra_core_width = 0; -static int ra_core_height = 0; -static int ra_tmp_width = 0; -static int ra_tmp_height = 0; -static int ra_set_core_hz = 0; -static int orig_width = 0; -static int orig_height = 0; +static unsigned ra_core_width = 0; +static unsigned ra_core_height = 0; +static unsigned ra_tmp_width = 0; +static unsigned ra_tmp_height = 0; +static unsigned ra_set_core_hz = 0; +static unsigned orig_width = 0; +static unsigned orig_height = 0; static int first_run = 0; static float ra_tmp_core_hz = 0.0f; @@ -178,51 +178,51 @@ static void crt_screen_setup_aspect(int width, int height) if (width < 1920) width = 640; height = 480; - crt_aspect_ratio_switch(width,height); + crt_aspect_ratio_switch(width, height); } if (height < 191 && height != 144) { - crt_aspect_ratio_switch(width,height); + crt_aspect_ratio_switch(width, height); height = 200; } if (height > 191) - crt_aspect_ratio_switch(width,height); + crt_aspect_ratio_switch(width, height); if (height == 144 && ra_set_core_hz == 50) { height = 288; - crt_aspect_ratio_switch(width,height); + crt_aspect_ratio_switch(width, height); } if (height > 200 && height < 224) { - crt_aspect_ratio_switch(width,height); + crt_aspect_ratio_switch(width, height); height = 224; } if (height > 224 && height < 240) { - crt_aspect_ratio_switch(width,height); + crt_aspect_ratio_switch(width, height); height = 240; } if (height > 240 && height < 255) { - crt_aspect_ratio_switch(width,height); + crt_aspect_ratio_switch(width, height); height = 254; } if (height == 528 && ra_set_core_hz == 60) { - crt_aspect_ratio_switch(width,height); + crt_aspect_ratio_switch(width, height); height = 480; } if (height >= 240 && height < 255 && ra_set_core_hz == 55) { - crt_aspect_ratio_switch(width,height); + crt_aspect_ratio_switch(width, height); height = 254; } @@ -230,7 +230,7 @@ static void crt_screen_setup_aspect(int width, int height) } -void crt_switch_res_core(int width, int height, float hz) +void crt_switch_res_core(unsigned width, unsigned height, float hz) { /* ra_core_hz float passed from within * void video_driver_monitor_adjust_system_rates(void) */ @@ -245,7 +245,7 @@ void crt_switch_res_core(int width, int height, float hz) (ra_tmp_height != ra_core_height) || (ra_core_width != ra_tmp_width) ) - crt_screen_setup_aspect(width,height); + crt_screen_setup_aspect(width, height); ra_tmp_height = ra_core_height; ra_tmp_width = ra_core_width; diff --git a/gfx/video_crt_switch.h b/gfx/video_crt_switch.h index 3f3c2ccbed..4e86ec4352 100644 --- a/gfx/video_crt_switch.h +++ b/gfx/video_crt_switch.h @@ -27,7 +27,7 @@ RETRO_BEGIN_DECLS -void crt_switch_res_core(int width, int height, float hz); +void crt_switch_res_core(unsigned width, unsigned height, float hz); void crt_video_restore(void); From bed4214059c3a316690a868dd125e6bec52dfad3 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 18:59:58 +0200 Subject: [PATCH 312/517] Turn int variables to unsigned --- gfx/video_crt_switch.c | 43 ++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index 722f4f6c20..eb030012ff 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -35,28 +35,31 @@ static unsigned ra_tmp_height = 0; static unsigned ra_set_core_hz = 0; static unsigned orig_width = 0; static unsigned orig_height = 0; -static int first_run = 0; -static float ra_tmp_core_hz = 0.0f; -static float fly_aspect = 0.0f; -static float ra_core_hz = 0.0f; +static bool first_run = true; + +static float ra_tmp_core_hz = 0.0f; +static float fly_aspect = 0.0f; +static float ra_core_hz = 0.0f; static void crt_check_first_run(void) { - if (first_run != 1) - { - /* Run of first boot to get current display resolution */ -#if defined(_WIN32) - orig_height = GetSystemMetrics(SM_CYSCREEN); - orig_width = GetSystemMetrics(SM_CXSCREEN); -#endif - } + if (!first_run) + return; - first_run = 1; + /* TODO/FIXME - do we want to set first_run back to true + * at some point in time or should it stay like this? */ + + /* Run of first boot to get current display resolution */ +#if defined(_WIN32) + orig_height = GetSystemMetrics(SM_CYSCREEN); + orig_width = GetSystemMetrics(SM_CXSCREEN); +#endif + first_run = false; } -static void crt_switch_res(int width, int height, - int f_restore,int ra_hz) +static void crt_switch_res(unsigned width, unsigned height, + int f_restore, int ra_hz) { /* Windows function to switch resolutions */ @@ -71,7 +74,7 @@ static void crt_switch_res(int width, int height, int depth = 0; if (f_restore == 0) - freq = ra_hz; + freq = ra_hz; EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); @@ -150,24 +153,24 @@ static void switch_crt_hz(void) ra_tmp_core_hz = ra_core_hz; } -static void crt_aspect_ratio_switch(int width, int height) +static void crt_aspect_ratio_switch(unsigned width, unsigned height) { /* send aspect float to videeo_driver */ fly_aspect = (float)width / height; video_driver_set_aspect_ratio_value((float)fly_aspect); } -static void switch_res_crt(int width, int height) +static void switch_res_crt(unsigned width, unsigned height) { if (height > 100) { - crt_switch_res(width, height,0,ra_set_core_hz); + crt_switch_res(width, height, 0, ra_set_core_hz); video_driver_apply_state_changes(); } } /* Create correct aspect to fit video if resolution does not exist */ -static void crt_screen_setup_aspect(int width, int height) +static void crt_screen_setup_aspect(unsigned width, unsigned height) { switch_crt_hz(); From c692f457c2582d37d8fd99c7603f01e0be83d41e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 19:25:00 +0200 Subject: [PATCH 313/517] Add switch_resolution to display_server interface --- gfx/display_servers/dispserv_null.c | 1 + gfx/display_servers/dispserv_win32.c | 86 +++++++++++++++++++++++++++- gfx/display_servers/dispserv_x11.c | 1 + gfx/video_crt_switch.c | 86 ++-------------------------- gfx/video_display_server.c | 8 +++ gfx/video_display_server.h | 10 ++++ 6 files changed, 109 insertions(+), 83 deletions(-) diff --git a/gfx/display_servers/dispserv_null.c b/gfx/display_servers/dispserv_null.c index aa02bcc9c4..65d46c5d12 100644 --- a/gfx/display_servers/dispserv_null.c +++ b/gfx/display_servers/dispserv_null.c @@ -49,6 +49,7 @@ const video_display_server_t dispserv_null = { null_set_window_opacity, null_set_window_progress, NULL, + NULL, "null" }; diff --git a/gfx/display_servers/dispserv_win32.c b/gfx/display_servers/dispserv_win32.c index ce72e247f9..901b500a46 100644 --- a/gfx/display_servers/dispserv_win32.c +++ b/gfx/display_servers/dispserv_win32.c @@ -185,9 +185,90 @@ static bool win32_set_window_decorations(void *data, bool on) { dispserv_win32_t *serv = (dispserv_win32_t*)data; - serv->decorations = on; + if (serv) + serv->decorations = on; - /* menu_setting performs a reinit instead to properly apply decoration changes */ + /* menu_setting performs a reinit instead to properly + * apply decoration changes */ + + return true; +} + +static bool win32_display_server_set_resolution(void *data, + unsigned width, unsigned height, int f_restore, int hz) +{ + LONG res; + DEVMODE curDevmode; + DEVMODE devmode; + + int iModeNum; + int freq = 0; + DWORD flags = 0; + int depth = 0; + dispserv_win32_t *serv = (dispserv_win32_t*)data; + + if (!serv) + return false; + + if (f_restore == 0) + freq = hz; + + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); + + /* used to stop superresolution bug */ + if (width == curDevmode.dmPelsWidth) + width = 0; + if (width == 0) + width = curDevmode.dmPelsWidth; + if (height == 0) + height = curDevmode.dmPelsHeight; + if (depth == 0) + depth = curDevmode.dmBitsPerPel; + if (freq == 0) + freq = curDevmode.dmDisplayFrequency; + + for (iModeNum = 0; ; iModeNum++) + { + if (!EnumDisplaySettings(NULL, iModeNum, &devmode)) + break; + + if (devmode.dmPelsWidth != width) + continue; + + if (devmode.dmPelsHeight != height) + continue; + + if (devmode.dmBitsPerPel != depth) + continue; + + if (devmode.dmDisplayFrequency != freq) + continue; + + devmode.dmFields |= + DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; + res = + win32_change_display_settings(NULL, &devmode, CDS_TEST); + + switch (res) + { + case DISP_CHANGE_SUCCESSFUL: + res = win32_change_display_settings(NULL, &devmode, flags); + switch (res) + { + case DISP_CHANGE_SUCCESSFUL: + return true; + case DISP_CHANGE_NOTUPDATED: + return true; + default: + break; + } + break; + case DISP_CHANGE_RESTART: + break; + default: + break; + } + } return true; } @@ -198,6 +279,7 @@ const video_display_server_t dispserv_win32 = { win32_set_window_opacity, win32_set_window_progress, win32_set_window_decorations, + win32_display_server_set_resolution, "win32" }; diff --git a/gfx/display_servers/dispserv_x11.c b/gfx/display_servers/dispserv_x11.c index 0d20343315..c9ce9293b8 100644 --- a/gfx/display_servers/dispserv_x11.c +++ b/gfx/display_servers/dispserv_x11.c @@ -78,6 +78,7 @@ const video_display_server_t dispserv_x11 = { x11_set_window_opacity, NULL, x11_set_window_decorations, + NULL, /* set_resolution */ "x11" }; diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index eb030012ff..558b6a525e 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -27,6 +27,7 @@ #include "video_driver.h" #include "video_crt_switch.h" +#include "video_display_server.h" static unsigned ra_core_width = 0; static unsigned ra_core_height = 0; @@ -58,85 +59,6 @@ static void crt_check_first_run(void) first_run = false; } -static void crt_switch_res(unsigned width, unsigned height, - int f_restore, int ra_hz) -{ - /* Windows function to switch resolutions */ - -#if defined(_WIN32) - LONG res; - DEVMODE curDevmode; - DEVMODE devmode; - - int iModeNum; - int freq = 0; - DWORD flags = 0; - int depth = 0; - - if (f_restore == 0) - freq = ra_hz; - - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); - - /* used to stop superresolution bug */ - if (width == curDevmode.dmPelsWidth) - width = 0; - if (width == 0) - width = curDevmode.dmPelsWidth; - if (height == 0) - height = curDevmode.dmPelsHeight; - if (depth == 0) - depth = curDevmode.dmBitsPerPel; - if (freq == 0) - freq = curDevmode.dmDisplayFrequency; - - for (iModeNum = 0; ; iModeNum++) - { - if (!EnumDisplaySettings(NULL, iModeNum, &devmode)) - break; - - if (devmode.dmPelsWidth != width) - continue; - - if (devmode.dmPelsHeight != height) - continue; - - if (devmode.dmBitsPerPel != depth) - continue; - - if (devmode.dmDisplayFrequency != freq) - continue; - - devmode.dmFields |= - DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; - res = - win32_change_display_settings(NULL, &devmode, CDS_TEST); - - switch (res) - { - case DISP_CHANGE_SUCCESSFUL: - res = win32_change_display_settings(NULL, &devmode, flags); - switch (res) - { - case DISP_CHANGE_SUCCESSFUL: - return; - case DISP_CHANGE_NOTUPDATED: - return; - default: - break; - } - break; - case DISP_CHANGE_RESTART: - break; - default: - break; - } - } -#elif defined(linux) - -#endif -} - static void switch_crt_hz(void) { if (ra_core_hz == ra_tmp_core_hz) @@ -164,7 +86,8 @@ static void switch_res_crt(unsigned width, unsigned height) { if (height > 100) { - crt_switch_res(width, height, 0, ra_set_core_hz); + video_display_server_switch_resolution(width, height, + 0, ra_set_core_hz); video_driver_apply_state_changes(); } } @@ -263,5 +186,6 @@ void crt_switch_res_core(unsigned width, unsigned height, float hz) void crt_video_restore(void) { - crt_switch_res(orig_width, orig_height, 0, 60); + video_display_server_switch_resolution(orig_width, orig_height, + 0, 60); } diff --git a/gfx/video_display_server.c b/gfx/video_display_server.c index 55baa165a2..296a10e264 100644 --- a/gfx/video_display_server.c +++ b/gfx/video_display_server.c @@ -81,3 +81,11 @@ bool video_display_server_set_window_decorations(bool on) return current_display_server->set_window_decorations(current_display_server_data, on); return false; } + +bool video_display_server_switch_resolution(unsigned width, unsigned height, + int f_restore, int hz) +{ + if (current_display_server && current_display_server->switch_resolution) + return current_display_server->switch_resolution(current_display_server_data, width, height, f_restore, hz); + return false; +} diff --git a/gfx/video_display_server.h b/gfx/video_display_server.h index 4f93a55e15..365223b30a 100644 --- a/gfx/video_display_server.h +++ b/gfx/video_display_server.h @@ -30,15 +30,25 @@ typedef struct video_display_server bool (*set_window_opacity)(void *data, unsigned opacity); bool (*set_window_progress)(void *data, int progress, bool finished); bool (*set_window_decorations)(void *data, bool on); + bool (*switch_resolution)(void *data, unsigned width, + unsigned height, int f_restore, int hz); const char *ident; } video_display_server_t; void* video_display_server_init(void); + void video_display_server_destroy(void); + bool video_display_server_set_window_opacity(unsigned opacity); + bool video_display_server_set_window_progress(int progress, bool finished); + bool video_display_server_set_window_decorations(bool on); +bool video_display_server_switch_resolution( + unsigned width, unsigned height, + int f_restore, int hz); + extern const video_display_server_t dispserv_win32; extern const video_display_server_t dispserv_x11; extern const video_display_server_t dispserv_null; From 953172f05b130e030e10a77b4a6f0c3d938e99e5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 19:29:07 +0200 Subject: [PATCH 314/517] This header include can go now --- gfx/video_crt_switch.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index 558b6a525e..874e5c3afa 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -22,7 +22,6 @@ #if defined(_WIN32) #include -#include "common/win32_common.h" #endif #include "video_driver.h" From 6d008107164d413511511c1e1e6876e6f303e956 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 15 Apr 2018 19:38:01 +0200 Subject: [PATCH 315/517] Simplify video_driver_monitor_adjust_system_rates --- gfx/video_driver.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/gfx/video_driver.c b/gfx/video_driver.c index bf5dd9b7b7..c17badc092 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -1414,9 +1414,10 @@ bool video_driver_cached_frame(void) void video_driver_monitor_adjust_system_rates(void) { - float timing_skew; + float timing_skew = 0.0f; settings_t *settings = config_get_ptr(); float video_refresh_rate = settings->floats.video_refresh_rate; + float timing_skew_hz = video_refresh_rate; const struct retro_system_timing *info = (const struct retro_system_timing*)&video_driver_av_info.timing; rarch_ctl(RARCH_CTL_UNSET_NONBLOCK_FORCED, NULL); @@ -1424,12 +1425,12 @@ void video_driver_monitor_adjust_system_rates(void) if (!info || info->fps <= 0.0) return; - video_driver_core_hz = info->fps; + video_driver_core_hz = info->fps; - if (video_driver_crt_switching_active) - timing_skew = fabs(1.0f - info->fps / video_driver_core_hz); - else - timing_skew = fabs(1.0f - info->fps / video_refresh_rate); + if (video_driver_crt_switching_active) + timing_skew_hz = video_driver_core_hz; + timing_skew = fabs( + 1.0f - info->fps / timing_skew_hz); /* We don't want to adjust pitch too much. If we have extreme cases, * just don't readjust at all. */ @@ -1439,17 +1440,9 @@ void video_driver_monitor_adjust_system_rates(void) RARCH_LOG("[Video]: Timings deviate too much. Will not adjust. (Display = %.2f Hz, Game = %.2f Hz)\n", video_refresh_rate, (float)info->fps); - - if (video_driver_crt_switching_active) - { - if (info->fps <= video_driver_core_hz) - return; - } - else - { - if (info->fps <= video_refresh_rate) - return; - } + + if (info->fps <= timing_skew_hz) + return; /* We won't be able to do VSync reliably when game FPS > monitor FPS. */ rarch_ctl(RARCH_CTL_SET_NONBLOCK_FORCED, NULL); From 7a005343782453505a40f7543ea44cc2ede67ff5 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Sun, 15 Apr 2018 11:12:56 -0500 Subject: [PATCH 316/517] Add poke interface for get_refresh_rate. --- gfx/drivers/caca_gfx.c | 1 + gfx/drivers/ctr_gfx.c | 1 + gfx/drivers/d3d10.c | 1 + gfx/drivers/d3d11.c | 1 + gfx/drivers/d3d12.c | 1 + gfx/drivers/d3d8.c | 1 + gfx/drivers/d3d9.c | 1 + gfx/drivers/dispmanx_gfx.c | 1 + gfx/drivers/drm_gfx.c | 1 + gfx/drivers/exynos_gfx.c | 1 + gfx/drivers/gdi_gfx.c | 1 + gfx/drivers/gl.c | 1 + gfx/drivers/gx2_gfx.c | 1 + gfx/drivers/gx_gfx.c | 1 + gfx/drivers/omap_gfx.c | 1 + gfx/drivers/psp1_gfx.c | 1 + gfx/drivers/sdl2_gfx.c | 1 + gfx/drivers/sdl_gfx.c | 1 + gfx/drivers/sunxi_gfx.c | 1 + gfx/drivers/switch_gfx.c | 1 + gfx/drivers/vga_gfx.c | 1 + gfx/drivers/vita2d_gfx.c | 1 + gfx/drivers/vulkan.c | 1 + gfx/drivers/xshm_gfx.c | 1 + gfx/video_driver.h | 1 + 25 files changed, 25 insertions(+) diff --git a/gfx/drivers/caca_gfx.c b/gfx/drivers/caca_gfx.c index ae50a65937..1738a877e5 100644 --- a/gfx/drivers/caca_gfx.c +++ b/gfx/drivers/caca_gfx.c @@ -318,6 +318,7 @@ static const video_poke_interface_t caca_poke_interface = { NULL, NULL, NULL, + NULL, caca_set_texture_frame, NULL, caca_set_osd_msg, diff --git a/gfx/drivers/ctr_gfx.c b/gfx/drivers/ctr_gfx.c index bdb3a69596..bec7fc01e2 100644 --- a/gfx/drivers/ctr_gfx.c +++ b/gfx/drivers/ctr_gfx.c @@ -1138,6 +1138,7 @@ static const video_poke_interface_t ctr_poke_interface = { ctr_load_texture, ctr_unload_texture, NULL, + NULL, ctr_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index ca9aab3304..d455113097 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -557,6 +557,7 @@ static const video_poke_interface_t d3d10_poke_interface = { NULL, /* load_texture */ NULL, /* unload_texture */ NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ d3d10_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 73730b8a4c..6578f32df9 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -1589,6 +1589,7 @@ static const video_poke_interface_t d3d11_poke_interface = { d3d11_gfx_load_texture, d3d11_gfx_unload_texture, NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ d3d11_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 16879caa8c..d7a8525f95 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -1752,6 +1752,7 @@ static const video_poke_interface_t d3d12_poke_interface = { d3d12_gfx_load_texture, d3d12_gfx_unload_texture, NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ d3d12_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index dcb0f51676..ddce718f09 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -1867,6 +1867,7 @@ static const video_poke_interface_t d3d_poke_interface = { d3d8_unload_texture, d3d8_set_video_mode, NULL, + NULL, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index 24b54dcf77..85d1cc55db 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -1891,6 +1891,7 @@ static const video_poke_interface_t d3d9_poke_interface = { d3d9_unload_texture, d3d9_set_video_mode, NULL, + NULL, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers/dispmanx_gfx.c b/gfx/drivers/dispmanx_gfx.c index 13c925ce79..7d80b453be 100644 --- a/gfx/drivers/dispmanx_gfx.c +++ b/gfx/drivers/dispmanx_gfx.c @@ -636,6 +636,7 @@ static const video_poke_interface_t dispmanx_poke_interface = { NULL, NULL, NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/drm_gfx.c b/gfx/drivers/drm_gfx.c index 6dda0b192c..41416b8b3a 100644 --- a/gfx/drivers/drm_gfx.c +++ b/gfx/drivers/drm_gfx.c @@ -969,6 +969,7 @@ static const video_poke_interface_t drm_poke_interface = { NULL, NULL, NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/exynos_gfx.c b/gfx/drivers/exynos_gfx.c index 91984819b8..78ed3502c7 100644 --- a/gfx/drivers/exynos_gfx.c +++ b/gfx/drivers/exynos_gfx.c @@ -1494,6 +1494,7 @@ static const video_poke_interface_t exynos_poke_interface = { NULL, NULL, NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/gdi_gfx.c b/gfx/drivers/gdi_gfx.c index 6ac92b19c4..f09884bdfb 100644 --- a/gfx/drivers/gdi_gfx.c +++ b/gfx/drivers/gdi_gfx.c @@ -538,6 +538,7 @@ static const video_poke_interface_t gdi_poke_interface = { NULL, NULL, gdi_set_video_mode, + NULL, /* get_refresh_rate */ NULL, gdi_get_video_output_size, gdi_get_video_output_prev, diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 260f776582..661cf4168b 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2601,6 +2601,7 @@ static const video_poke_interface_t gl_poke_interface = { gl_load_texture, gl_unload_texture, gl_set_video_mode, + NULL, /* get_refresh_rate */ NULL, gl_get_video_output_size, gl_get_video_output_prev, diff --git a/gfx/drivers/gx2_gfx.c b/gfx/drivers/gx2_gfx.c index 9e8c4e16c2..853e1bdb56 100644 --- a/gfx/drivers/gx2_gfx.c +++ b/gfx/drivers/gx2_gfx.c @@ -1718,6 +1718,7 @@ static const video_poke_interface_t wiiu_poke_interface = wiiu_gfx_load_texture, wiiu_gfx_unload_texture, NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ wiiu_gfx_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/gx_gfx.c b/gfx/drivers/gx_gfx.c index c14d1a7f0c..e07294f0ed 100644 --- a/gfx/drivers/gx_gfx.c +++ b/gfx/drivers/gx_gfx.c @@ -1271,6 +1271,7 @@ static const video_poke_interface_t gx_poke_interface = { NULL, NULL, gx_set_video_mode, + NULL, /* get_refresh_rate */ NULL, gx_get_video_output_size, gx_get_video_output_prev, diff --git a/gfx/drivers/omap_gfx.c b/gfx/drivers/omap_gfx.c index a97d30f198..9c650f27a0 100644 --- a/gfx/drivers/omap_gfx.c +++ b/gfx/drivers/omap_gfx.c @@ -1135,6 +1135,7 @@ static const video_poke_interface_t omap_gfx_poke_interface = { NULL, NULL, NULL, + NULL, /* get_refresh_rate */ NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/psp1_gfx.c b/gfx/drivers/psp1_gfx.c index 5dc0dd1f74..7a2e49b77a 100644 --- a/gfx/drivers/psp1_gfx.c +++ b/gfx/drivers/psp1_gfx.c @@ -836,6 +836,7 @@ static const video_poke_interface_t psp_poke_interface = { NULL, NULL, NULL, + NULL, /* get_refresh_rate */ psp_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/sdl2_gfx.c b/gfx/drivers/sdl2_gfx.c index 9e6074bd79..bbed0bbc68 100644 --- a/gfx/drivers/sdl2_gfx.c +++ b/gfx/drivers/sdl2_gfx.c @@ -726,6 +726,7 @@ static video_poke_interface_t sdl2_video_poke_interface = { NULL, NULL, NULL, + NULL, /* get_refresh_rate */ sdl2_poke_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/sdl_gfx.c b/gfx/drivers/sdl_gfx.c index 918d0ec66c..5f5978ae6e 100644 --- a/gfx/drivers/sdl_gfx.c +++ b/gfx/drivers/sdl_gfx.c @@ -522,6 +522,7 @@ static const video_poke_interface_t sdl_poke_interface = { NULL, NULL, NULL, + NULL, /* get_refresh_rate */ sdl_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/sunxi_gfx.c b/gfx/drivers/sunxi_gfx.c index 5c71710831..9d6e786deb 100644 --- a/gfx/drivers/sunxi_gfx.c +++ b/gfx/drivers/sunxi_gfx.c @@ -937,6 +937,7 @@ static const video_poke_interface_t sunxi_poke_interface = { NULL, NULL, NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/switch_gfx.c b/gfx/drivers/switch_gfx.c index 83d22dddb0..19d646279f 100644 --- a/gfx/drivers/switch_gfx.c +++ b/gfx/drivers/switch_gfx.c @@ -392,6 +392,7 @@ static const video_poke_interface_t switch_poke_interface = { NULL, /* load_texture */ NULL, /* unload_texture */ NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/vga_gfx.c b/gfx/drivers/vga_gfx.c index d9cdcb9182..6ff8e98d4d 100644 --- a/gfx/drivers/vga_gfx.c +++ b/gfx/drivers/vga_gfx.c @@ -403,6 +403,7 @@ static const video_poke_interface_t vga_poke_interface = { NULL, NULL, NULL, + NULL, vga_set_texture_frame, NULL, vga_set_osd_msg, diff --git a/gfx/drivers/vita2d_gfx.c b/gfx/drivers/vita2d_gfx.c index 50692b62e5..179fdbaaf9 100644 --- a/gfx/drivers/vita2d_gfx.c +++ b/gfx/drivers/vita2d_gfx.c @@ -795,6 +795,7 @@ static const video_poke_interface_t vita_poke_interface = { vita_load_texture, vita_unload_texture, NULL, + NULL, /* get_refresh_rate */ vita_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 0c6ed1c4b0..9d596941e8 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -2298,6 +2298,7 @@ static const video_poke_interface_t vulkan_poke_interface = { vulkan_load_texture, vulkan_unload_texture, vulkan_set_video_mode, + NULL, /* get_refresh_rate */ NULL, NULL, NULL, diff --git a/gfx/drivers/xshm_gfx.c b/gfx/drivers/xshm_gfx.c index 09121a414d..218d670e22 100644 --- a/gfx/drivers/xshm_gfx.c +++ b/gfx/drivers/xshm_gfx.c @@ -209,6 +209,7 @@ static video_poke_interface_t xshm_video_poke_interface = { NULL, NULL, NULL, + NULL, /* get_refresh_rate */ xshm_poke_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/video_driver.h b/gfx/video_driver.h index d1a2551986..8f84180655 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -701,6 +701,7 @@ typedef struct video_poke_interface void (*unload_texture)(void *data, uintptr_t id); void (*set_video_mode)(void *data, unsigned width, unsigned height, bool fullscreen); + float (*get_refresh_rate)(void *data); void (*set_filtering)(void *data, unsigned index, bool smooth); void (*get_video_output_size)(void *data, unsigned *width, unsigned *height); From 533f8981328cbb4090722a7c396208435960296e Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Sun, 15 Apr 2018 17:38:00 -0500 Subject: [PATCH 317/517] Add get refresh rate context function. Implement for X11 and Wayland. --- gfx/common/x11_common.c | 17 +++++++++++++++++ gfx/common/x11_common.h | 2 ++ gfx/drivers/gl.c | 10 +++++++++- gfx/drivers/vulkan.c | 12 +++++++++++- gfx/drivers/xshm_gfx.c | 9 +++++++-- gfx/drivers_context/android_ctx.c | 1 + gfx/drivers_context/cgl_ctx.c | 1 + gfx/drivers_context/drm_ctx.c | 1 + gfx/drivers_context/emscriptenegl_ctx.c | 1 + gfx/drivers_context/gdi_ctx.c | 1 + gfx/drivers_context/gfx_null_ctx.c | 1 + gfx/drivers_context/khr_display_ctx.c | 1 + gfx/drivers_context/mali_fbdev_ctx.c | 1 + gfx/drivers_context/opendingux_fbdev_ctx.c | 1 + gfx/drivers_context/osmesa_ctx.c | 1 + gfx/drivers_context/ps3_ctx.c | 1 + gfx/drivers_context/qnx_ctx.c | 1 + gfx/drivers_context/sdl_gl_ctx.c | 1 + gfx/drivers_context/vc_egl_ctx.c | 1 + gfx/drivers_context/vivante_fbdev_ctx.c | 1 + gfx/drivers_context/wayland_ctx.c | 10 ++++++++++ gfx/drivers_context/wgl_ctx.c | 1 + gfx/drivers_context/x_ctx.c | 1 + gfx/drivers_context/xegl_ctx.c | 1 + gfx/video_driver.c | 18 ++++++++++++++++++ gfx/video_driver.h | 6 ++++++ gfx/video_thread_wrapper.c | 1 + 27 files changed, 99 insertions(+), 4 deletions(-) diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index 6de3e8a3d7..97ec6adb08 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -234,6 +234,23 @@ void x11_suspend_screensaver(Window wnd, bool enable) x11_suspend_screensaver_xdg_screensaver(wnd, enable); } +float x11_get_refresh_rate(void *data) +{ + XWindowAttributes attr; + XF86VidModeModeLine modeline; + Screen *screen; + int screenid; + int dotclock; + + XGetWindowAttributes(g_x11_dpy, g_x11_win, &attr); + screen = attr.screen; + screenid = XScreenNumberOfScreen(screen); + + XF86VidModeGetModeLine(g_x11_dpy, screenid, &dotclock, &modeline); + + return (float) dotclock * 1000.0f / modeline.htotal / modeline.vtotal; +} + static bool get_video_mode(video_frame_info_t *video_info, Display *dpy, unsigned width, unsigned height, XF86VidModeModeInfo *mode, XF86VidModeModeInfo *desktop_mode) diff --git a/gfx/common/x11_common.h b/gfx/common/x11_common.h index 6be78245d1..3aa8117075 100644 --- a/gfx/common/x11_common.h +++ b/gfx/common/x11_common.h @@ -48,6 +48,8 @@ void x11_destroy_input_context(XIM *xim, XIC *xic); bool x11_get_metrics(void *data, enum display_metric_types type, float *value); +float x11_get_refresh_rate(void *data); + void x11_check_window(void *data, bool *quit, bool *resize, unsigned *width, unsigned *height, bool is_shutdown); diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 661cf4168b..ef6be56ece 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2586,6 +2586,14 @@ static void gl_set_coords(void *handle_data, void *shader_data, shader_data, coords); } +static float gl_get_refresh_rate(void *data) +{ + float refresh_rate; + if (video_context_driver_get_refresh_rate(&refresh_rate)) + return refresh_rate; + return 0.0f; +} + static void gl_set_mvp(void *data, void *shader_data, const void *mat_data) { @@ -2601,7 +2609,7 @@ static const video_poke_interface_t gl_poke_interface = { gl_load_texture, gl_unload_texture, gl_set_video_mode, - NULL, /* get_refresh_rate */ + gl_get_refresh_rate, NULL, gl_get_video_output_size, gl_get_video_output_prev, diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 9d596941e8..c6c2aac6d6 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -2292,13 +2292,23 @@ static void vulkan_unload_texture(void *data, uintptr_t handle) free(texture); } +static float vulkan_get_refresh_rate(void *data) +{ + float refresh_rate; + + if (video_context_driver_get_refresh_rate(&refresh_rate)) + return refresh_rate; + + return 0.0f; +} + static const video_poke_interface_t vulkan_poke_interface = { NULL, /* set_coords */ NULL, /* set_mvp */ vulkan_load_texture, vulkan_unload_texture, vulkan_set_video_mode, - NULL, /* get_refresh_rate */ + vulkan_get_refresh_rate, /* get_refresh_rate */ NULL, NULL, NULL, diff --git a/gfx/drivers/xshm_gfx.c b/gfx/drivers/xshm_gfx.c index 218d670e22..6385f591ed 100644 --- a/gfx/drivers/xshm_gfx.c +++ b/gfx/drivers/xshm_gfx.c @@ -189,7 +189,7 @@ static void xshm_poke_texture_enable(void *data, static void xshm_poke_set_osd_msg(void *data, video_frame_info_t *video_info, const char *msg, - const struct font_params *params, void *font) + const void *params, void *font) { } @@ -203,13 +203,18 @@ static void xshm_grab_mouse_toggle(void *data) } +static float xshm_poke_get_refresh_rate(void *data) +{ + return x11_get_refresh_rate(data); +} + static video_poke_interface_t xshm_video_poke_interface = { NULL, /* set_coords */ NULL, /* set_mvp */ NULL, NULL, NULL, - NULL, /* get_refresh_rate */ + xshm_poke_get_refresh_rate, xshm_poke_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers_context/android_ctx.c b/gfx/drivers_context/android_ctx.c index ca0f7911e1..422dc821cf 100644 --- a/gfx/drivers_context/android_ctx.c +++ b/gfx/drivers_context/android_ctx.c @@ -602,6 +602,7 @@ const gfx_ctx_driver_t gfx_ctx_android = { android_gfx_ctx_set_swap_interval, android_gfx_ctx_set_video_mode, android_gfx_ctx_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/cgl_ctx.c b/gfx/drivers_context/cgl_ctx.c index faeb63fc11..fb26355304 100644 --- a/gfx/drivers_context/cgl_ctx.c +++ b/gfx/drivers_context/cgl_ctx.c @@ -343,6 +343,7 @@ const gfx_ctx_driver_t gfx_ctx_cgl = { gfx_ctx_cgl_swap_interval, gfx_ctx_cgl_set_video_mode, gfx_ctx_cgl_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c index 2300815c18..a4937368f1 100644 --- a/gfx/drivers_context/drm_ctx.c +++ b/gfx/drivers_context/drm_ctx.c @@ -908,6 +908,7 @@ const gfx_ctx_driver_t gfx_ctx_drm = { gfx_ctx_drm_swap_interval, gfx_ctx_drm_set_video_mode, gfx_ctx_drm_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/emscriptenegl_ctx.c b/gfx/drivers_context/emscriptenegl_ctx.c index 1bc927e334..9e3234d86f 100644 --- a/gfx/drivers_context/emscriptenegl_ctx.c +++ b/gfx/drivers_context/emscriptenegl_ctx.c @@ -376,6 +376,7 @@ const gfx_ctx_driver_t gfx_ctx_emscripten = { gfx_ctx_emscripten_swap_interval, gfx_ctx_emscripten_set_video_mode, gfx_ctx_emscripten_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/gdi_ctx.c b/gfx/drivers_context/gdi_ctx.c index 6316df83a8..d251d75cb9 100644 --- a/gfx/drivers_context/gdi_ctx.c +++ b/gfx/drivers_context/gdi_ctx.c @@ -352,6 +352,7 @@ const gfx_ctx_driver_t gfx_ctx_gdi = { gfx_ctx_gdi_swap_interval, gfx_ctx_gdi_set_video_mode, gfx_ctx_gdi_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/gfx_null_ctx.c b/gfx/drivers_context/gfx_null_ctx.c index f7884ad08c..cf0c2cc742 100644 --- a/gfx/drivers_context/gfx_null_ctx.c +++ b/gfx/drivers_context/gfx_null_ctx.c @@ -141,6 +141,7 @@ const gfx_ctx_driver_t gfx_ctx_null = { gfx_ctx_null_swap_interval, gfx_ctx_null_set_video_mode, gfx_ctx_null_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/khr_display_ctx.c b/gfx/drivers_context/khr_display_ctx.c index c597383fee..0d671bbe4a 100644 --- a/gfx/drivers_context/khr_display_ctx.c +++ b/gfx/drivers_context/khr_display_ctx.c @@ -236,6 +236,7 @@ const gfx_ctx_driver_t gfx_ctx_khr_display = { gfx_ctx_khr_display_set_swap_interval, gfx_ctx_khr_display_set_video_mode, gfx_ctx_khr_display_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/mali_fbdev_ctx.c b/gfx/drivers_context/mali_fbdev_ctx.c index 565796345d..b30de94dcf 100644 --- a/gfx/drivers_context/mali_fbdev_ctx.c +++ b/gfx/drivers_context/mali_fbdev_ctx.c @@ -294,6 +294,7 @@ const gfx_ctx_driver_t gfx_ctx_mali_fbdev = { gfx_ctx_mali_fbdev_set_swap_interval, gfx_ctx_mali_fbdev_set_video_mode, gfx_ctx_mali_fbdev_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/opendingux_fbdev_ctx.c b/gfx/drivers_context/opendingux_fbdev_ctx.c index 5d6c0bf820..69ff6a58c6 100644 --- a/gfx/drivers_context/opendingux_fbdev_ctx.c +++ b/gfx/drivers_context/opendingux_fbdev_ctx.c @@ -271,6 +271,7 @@ const gfx_ctx_driver_t gfx_ctx_opendingux_fbdev = { gfx_ctx_opendingux_set_swap_interval, gfx_ctx_opendingux_set_video_mode, gfx_ctx_opendingux_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/osmesa_ctx.c b/gfx/drivers_context/osmesa_ctx.c index df460a14d5..a3ffaf5957 100644 --- a/gfx/drivers_context/osmesa_ctx.c +++ b/gfx/drivers_context/osmesa_ctx.c @@ -398,6 +398,7 @@ const gfx_ctx_driver_t gfx_ctx_osmesa = osmesa_ctx_swap_interval, osmesa_ctx_set_video_mode, osmesa_ctx_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/ps3_ctx.c b/gfx/drivers_context/ps3_ctx.c index 9ff04dd84f..1a5dd2c0ff 100644 --- a/gfx/drivers_context/ps3_ctx.c +++ b/gfx/drivers_context/ps3_ctx.c @@ -421,6 +421,7 @@ const gfx_ctx_driver_t gfx_ctx_ps3 = { gfx_ctx_ps3_get_video_output_size, gfx_ctx_ps3_get_video_output_prev, gfx_ctx_ps3_get_video_output_next, + NULL, /* get_refresh_rate */ NULL, /* get_metrics */ NULL, NULL, /* update_title */ diff --git a/gfx/drivers_context/qnx_ctx.c b/gfx/drivers_context/qnx_ctx.c index a425aebdac..2ce96ac7aa 100644 --- a/gfx/drivers_context/qnx_ctx.c +++ b/gfx/drivers_context/qnx_ctx.c @@ -471,6 +471,7 @@ const gfx_ctx_driver_t gfx_ctx_qnx = { gfx_ctx_qnx_set_swap_interval, gfx_ctx_qnx_set_video_mode, gfx_ctx_qnx_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/sdl_gl_ctx.c b/gfx/drivers_context/sdl_gl_ctx.c index 5801c4e9a9..aedb29e5dc 100644 --- a/gfx/drivers_context/sdl_gl_ctx.c +++ b/gfx/drivers_context/sdl_gl_ctx.c @@ -422,6 +422,7 @@ const gfx_ctx_driver_t gfx_ctx_sdl_gl = sdl_ctx_swap_interval, sdl_ctx_set_video_mode, sdl_ctx_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/vc_egl_ctx.c b/gfx/drivers_context/vc_egl_ctx.c index a485eb4e43..e507056aed 100644 --- a/gfx/drivers_context/vc_egl_ctx.c +++ b/gfx/drivers_context/vc_egl_ctx.c @@ -712,6 +712,7 @@ const gfx_ctx_driver_t gfx_ctx_videocore = { gfx_ctx_vc_set_swap_interval, gfx_ctx_vc_set_video_mode, gfx_ctx_vc_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/vivante_fbdev_ctx.c b/gfx/drivers_context/vivante_fbdev_ctx.c index 7176ce976b..512e740add 100644 --- a/gfx/drivers_context/vivante_fbdev_ctx.c +++ b/gfx/drivers_context/vivante_fbdev_ctx.c @@ -277,6 +277,7 @@ const gfx_ctx_driver_t gfx_ctx_vivante_fbdev = { gfx_ctx_vivante_set_swap_interval, gfx_ctx_vivante_set_video_mode, gfx_ctx_vivante_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/wayland_ctx.c b/gfx/drivers_context/wayland_ctx.c index 0b5e3d7a26..07e1d7b3aa 100644 --- a/gfx/drivers_context/wayland_ctx.c +++ b/gfx/drivers_context/wayland_ctx.c @@ -58,6 +58,7 @@ typedef struct gfx_ctx_wayland_data unsigned height; unsigned physical_width; unsigned physical_height; + int refresh_rate; struct wl_registry *registry; struct wl_compositor *compositor; struct wl_surface *surface; @@ -440,6 +441,7 @@ static void display_handle_mode(void *data, gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; wl->width = width; wl->height = height; + wl->refresh_rate = refresh; /* Certain older Wayland implementations report in Hz, * but it should be mHz. */ @@ -1369,6 +1371,13 @@ static void gfx_ctx_wl_show_mouse(void *data, bool state) wl->cursor.visible = state; } +static float gfx_ctx_wl_get_refresh_rate(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + return (float) wl->refresh_rate * 1000.0f; +} + const gfx_ctx_driver_t gfx_ctx_wayland = { gfx_ctx_wl_init, gfx_ctx_wl_destroy, @@ -1377,6 +1386,7 @@ const gfx_ctx_driver_t gfx_ctx_wayland = { gfx_ctx_wl_set_swap_interval, gfx_ctx_wl_set_video_mode, gfx_ctx_wl_get_video_size, + gfx_ctx_wl_get_refresh_rate, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/wgl_ctx.c b/gfx/drivers_context/wgl_ctx.c index 1545862bd3..a698eadfb3 100644 --- a/gfx/drivers_context/wgl_ctx.c +++ b/gfx/drivers_context/wgl_ctx.c @@ -760,6 +760,7 @@ const gfx_ctx_driver_t gfx_ctx_wgl = { gfx_ctx_wgl_swap_interval, gfx_ctx_wgl_set_video_mode, gfx_ctx_wgl_get_video_size, + NULL, /* get_refresh_rate */ gfx_ctx_wgl_get_video_output_size, gfx_ctx_wgl_get_video_output_prev, gfx_ctx_wgl_get_video_output_next, diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index e623f6c287..62051b43d8 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -1225,6 +1225,7 @@ const gfx_ctx_driver_t gfx_ctx_x = { gfx_ctx_x_swap_interval, gfx_ctx_x_set_video_mode, x11_get_video_size, + x11_get_refresh_rate, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/drivers_context/xegl_ctx.c b/gfx/drivers_context/xegl_ctx.c index 04485648e2..45df7e35c1 100644 --- a/gfx/drivers_context/xegl_ctx.c +++ b/gfx/drivers_context/xegl_ctx.c @@ -621,6 +621,7 @@ const gfx_ctx_driver_t gfx_ctx_x_egl = gfx_ctx_xegl_set_swap_interval, gfx_ctx_xegl_set_video_mode, x11_get_video_size, + x11_get_refresh_rate, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ diff --git a/gfx/video_driver.c b/gfx/video_driver.c index c17badc092..60f475de59 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -3189,6 +3189,16 @@ bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics) return false; } +bool video_context_driver_get_refresh_rate(float *refresh_rate) +{ + if (!current_video_context.get_refresh_rate || !refresh_rate) + return false; + + *refresh_rate = current_video_context.get_refresh_rate(video_context_data); + + return true; +} + bool video_context_driver_input_driver(gfx_ctx_input_t *inp) { settings_t *settings = config_get_ptr(); @@ -3684,3 +3694,11 @@ void video_driver_set_mvp(video_shader_ctx_mvp_t *mvp) video_driver_poke->set_mvp(mvp->data, current_shader_data, mvp->matrix); } } + +float video_driver_get_refresh_rate(void) +{ + if (video_driver_poke && video_driver_poke->get_refresh_rate) + return video_driver_poke->get_refresh_rate(video_driver_data); + + return 0.0f; +} diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 8f84180655..0d2336d003 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -526,6 +526,8 @@ typedef struct gfx_ctx_driver * If not initialized yet, it returns current screen size. */ void (*get_video_size)(void*, unsigned*, unsigned*); + float (*get_refresh_rate)(void*); + void (*get_video_output_size)(void*, unsigned*, unsigned*); void (*get_video_output_prev)(void*); @@ -1275,6 +1277,8 @@ bool video_context_driver_set_video_mode(gfx_ctx_mode_t *mode_info); bool video_context_driver_get_video_size(gfx_ctx_mode_t *mode_info); +bool video_context_driver_get_refresh_rate(float *refresh_rate); + bool video_context_driver_get_context_data(void *data); bool video_context_driver_show_mouse(bool *bool_data); @@ -1333,6 +1337,8 @@ void video_shader_driver_use(void *data); bool video_shader_driver_wrap_type(video_shader_ctx_wrap_t *wrap); +float video_driver_get_refresh_rate(void); + extern bool (*video_driver_cb_has_focus)(void); extern video_driver_t video_gl; diff --git a/gfx/video_thread_wrapper.c b/gfx/video_thread_wrapper.c index 027e4a0daf..e6216a6d8c 100644 --- a/gfx/video_thread_wrapper.c +++ b/gfx/video_thread_wrapper.c @@ -1258,6 +1258,7 @@ static const video_poke_interface_t thread_poke = { thread_load_texture, thread_unload_texture, thread_set_video_mode, + NULL, thread_set_filtering, thread_get_video_output_size, thread_get_video_output_prev, From 20d87347f7c4d52963fb7a8ae9e8d56310fc3f52 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Sun, 15 Apr 2018 17:53:46 -0500 Subject: [PATCH 318/517] Add menu item showing and allowing to automatically set precise refresh rate. --- intl/msg_hash_ar.h | 4 ++++ intl/msg_hash_chs.h | 4 ++++ intl/msg_hash_cht.h | 4 ++++ intl/msg_hash_de.h | 4 ++++ intl/msg_hash_eo.h | 4 ++++ intl/msg_hash_es.h | 8 ++++++++ intl/msg_hash_fr.h | 4 ++++ intl/msg_hash_it.h | 4 ++++ intl/msg_hash_ja.h | 4 ++++ intl/msg_hash_ko.h | 4 ++++ intl/msg_hash_lbl.h | 2 ++ intl/msg_hash_nl.h | 4 ++++ intl/msg_hash_pl.h | 4 ++++ intl/msg_hash_pt_br.h | 6 ++++++ intl/msg_hash_pt_pt.h | 4 ++++ intl/msg_hash_ru.h | 4 ++++ intl/msg_hash_us.c | 7 +++++++ intl/msg_hash_us.h | 4 ++++ intl/msg_hash_vn.h | 4 ++++ menu/cbs/menu_cbs_ok.c | 21 ++++++++++++++++++++ menu/cbs/menu_cbs_sublabel.c | 4 ++++ menu/menu_cbs.h | 2 ++ menu/menu_displaylist.c | 3 +++ menu/menu_setting.c | 37 ++++++++++++++++++++++++++++++++++++ msg_hash.h | 1 + 25 files changed, 151 insertions(+) diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index 49a95bd27c..81731a8e3e 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -1747,6 +1747,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Vertical Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Estimated Screen Framerate") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotation") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1995,6 +1997,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Selects which display screen to use.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "The accurate estimated refresh rate of the screen in Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Change video output settings.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index 9ac1f14fc7..2f1ae124ce 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -1626,6 +1626,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "刷新率") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "估算的显示器帧率") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "旋转") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1857,6 +1859,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "选择将要使用哪一个显示器。") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "估算的显示器刷新率(Hz)。") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "调整视频输出的选项。") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index b54635d9a0..84bcb57f71 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -1626,6 +1626,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "刷新率") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "估算的顯示器幀率") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "旋轉") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1849,6 +1851,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "選擇將要使用哪一個顯示器。") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "估算的顯示器刷新率(Hz)。") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "調整視訊輸出的選項。") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_de.h b/intl/msg_hash_de.h index 311affa126..bf0784201b 100644 --- a/intl/msg_hash_de.h +++ b/intl/msg_hash_de.h @@ -1673,6 +1673,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Bildwiederholrate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Geschätzte Bildwiederholrate") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotation") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1911,6 +1913,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Wält den Bildschirm aus, der für RetroArch verwendet wird.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "Die geschätzte Bildwiederholrate des Bildschirms in Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Einstellungen für die Videoausgabe anpassen.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 89dceddae4..5001ca8138 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -1526,6 +1526,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Vertical Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Estimated Screen Framerate") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotation") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1750,6 +1752,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Selects which display screen to use.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "The accurate estimated refresh rate of the screen in Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Adjusts settings for video output.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_es.h b/intl/msg_hash_es.h index 5371d79e03..e8d8434dc9 100644 --- a/intl/msg_hash_es.h +++ b/intl/msg_hash_es.h @@ -2953,6 +2953,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Frecuencia estimada del monitor" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotación" @@ -3441,6 +3445,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "Estimado preciso de refresco de la pantalla en Hz" ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver." + ) MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Opciones de salida de video" diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index 5950a9a29b..0b24993d02 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -1643,6 +1643,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Fréquence de rafraîchissement verticale") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Fréquence estimée de l'écran") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotation") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1873,6 +1875,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Sélectionne l'écran à utiliser.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "Taux de rafraîchissement estimé de l'écran en Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Ajuster les paramètres de sortie vidéo.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index a81f223b8c..99f7321f26 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -1671,6 +1671,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Frequenza di aggiornamento verticale") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Framerate dello schermo stimato") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotazione") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1909,6 +1911,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Seleziona lo schermo da utilizzare.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "La stima precisa di aggiornamento dello schermo in Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Cambia le impostazioni per l'uscita video.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index 4bb835f10e..caa25ad2c3 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -1737,6 +1737,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "リフレッシュレート") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "画面の予想フレームレート") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "回転") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1975,6 +1977,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "希望する画面を選択する。") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "画面の正確な推定のリフレッシュレート") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "ビデオ出力の設定を変える。") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_ko.h b/intl/msg_hash_ko.h index b900c8370d..8c148efd8f 100644 --- a/intl/msg_hash_ko.h +++ b/intl/msg_hash_ko.h @@ -1621,6 +1621,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_POST_FILTER_RECORD, "필터 적용된 녹화 사용") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "수직 리프레시 비율") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "화면 프레임레이트 측정치") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, @@ -1853,6 +1855,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "사용할 디스플레이를 선택.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "화면의 정확한 리프레시 비율(Hz) 측정치.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "비디오 출력 설정 변경.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 5178e970ba..3f38faca13 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1157,6 +1157,8 @@ MSG_HASH(MENU_ENUM_LABEL_VIDEO_REFRESH_RATE, "video_refresh_rate") MSG_HASH(MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_AUTO, "video_refresh_rate_auto") +MSG_HASH(MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_POLLED, + "video_refresh_rate_polled") MSG_HASH(MENU_ENUM_LABEL_VIDEO_ROTATION, "video_rotation") MSG_HASH(MENU_ENUM_LABEL_VIDEO_SCALE, diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index bcb12f83ac..49db7b137f 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -1524,6 +1524,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Vertical Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Geschatte Scherm Framerate") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotatie") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1748,6 +1750,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Selects which display screen to use.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "The accurate estimated refresh rate of the screen in Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Adjusts settings for video output.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index 40167dd3d5..f3624789ca 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -1753,6 +1753,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Odświeżanie w pionie") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Szacowana liczba klatek na sekundę na ekranie") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Obrót") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -2003,6 +2005,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Określa, który ekran wyświetlacza ma być używany.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "Dokładna szacowana częstotliwość odświeżania ekranu w Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Zmień ustawienia wyjścia wideo.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index 73513133b9..8a6b593ab7 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -2197,6 +2197,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Taxa de Quadros Estimada da Tela" ) +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate" + ) MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotação" ) @@ -2549,6 +2552,9 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "A taxa de atualização estimada da tela em Hz." ) +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver." + ) MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Alterar as configurações de saída de vídeo." ) diff --git a/intl/msg_hash_pt_pt.h b/intl/msg_hash_pt_pt.h index 3e50847c9a..842f2b9bf1 100644 --- a/intl/msg_hash_pt_pt.h +++ b/intl/msg_hash_pt_pt.h @@ -1615,6 +1615,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Taxa de atualização") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Taxa de atualização estimada do ecrã") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotação") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1843,6 +1845,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Seleciona o ecrã a ser utilizado.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "A taxa de atualização do ecrã estimada em Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Alterar as definições da saída de vídeo.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_ru.h b/intl/msg_hash_ru.h index 52eaa1288a..5278ff7c4a 100644 --- a/intl/msg_hash_ru.h +++ b/intl/msg_hash_ru.h @@ -1652,6 +1652,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Вертикальная частота обновления") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Оценочная частота экрана") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Вращение") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1888,6 +1890,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Выбирает, какой экран дисплея использовать.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "Точная оценка частоты обновления экрана в Гц.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Настройка параметров вывода видео.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 2b2d49b54f..5acc456d3f 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -1203,6 +1203,13 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) "not run at 60Hz, or something close to it, \n" "disable VSync, and leave this at its default."); break; + case MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_POLLED: + snprintf(s, len, + "Set Polled Refresh Rate\n" + " \n" + "Sets the refresh rate to the actual value\n" + "polled from the display driver."); + break; case MENU_ENUM_LABEL_VIDEO_ROTATION: snprintf(s, len, "Forces a certain rotation \n" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 35682e25c5..c6de19a42a 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1763,6 +1763,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Vertical Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Estimated Screen Framerate") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotation") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -2013,6 +2015,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Selects which display screen to use.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "The accurate estimated refresh rate of the screen in Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Change video output settings.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index 0fee79bf3a..efc435f26a 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -1639,6 +1639,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE, "Vertical Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_AUTO, "Estimated Screen Framerate") +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + "Set Display-Reported Refresh Rate") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION, "Rotation") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, @@ -1869,6 +1871,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX, "Chọn màn hình hiển thị để sử dụng.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO, "The accurate estimated refresh rate of the screen in Hz.") +MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED, + "The refresh rate as reported by the display driver.") MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS, "Điều chỉnh thiết lập cho video ra.") MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS, diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 8cab13ff5b..f0296a7ced 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -154,6 +154,27 @@ int setting_action_ok_video_refresh_rate_auto(void *data, bool wraparound) return 0; } +int setting_action_ok_video_refresh_rate_polled(void *data, bool wraparound) +{ + rarch_setting_t *setting = (rarch_setting_t*)data; + float refresh_rate = 0.0; + + if (!setting) + return -1; + + if ((refresh_rate = video_driver_get_refresh_rate()) == 0.0) + return -1; + + driver_ctl(RARCH_DRIVER_CTL_SET_REFRESH_RATE, &refresh_rate); + /* Incase refresh rate update forced non-block video. */ + command_event(CMD_EVENT_VIDEO_SET_BLOCKING_STATE, NULL); + + if (setting_generic_action_ok_default(setting, wraparound) != 0) + return -1; + + return 0; +} + int setting_action_ok_bind_all(void *data, bool wraparound) { (void)wraparound; diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index d5b83c6c8c..1f59d48752 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -132,6 +132,7 @@ default_sublabel_macro(action_bind_sublabel_core_allow_rotate, MENU_ default_sublabel_macro(action_bind_sublabel_dummy_on_core_shutdown, MENU_ENUM_SUBLABEL_DUMMY_ON_CORE_SHUTDOWN) default_sublabel_macro(action_bind_sublabel_dummy_check_missing_firmware, MENU_ENUM_SUBLABEL_CHECK_FOR_MISSING_FIRMWARE) default_sublabel_macro(action_bind_sublabel_video_refresh_rate, MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE) +default_sublabel_macro(action_bind_sublabel_video_refresh_rate_polled, MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED) default_sublabel_macro(action_bind_sublabel_audio_enable, MENU_ENUM_SUBLABEL_AUDIO_ENABLE) default_sublabel_macro(action_bind_sublabel_audio_max_timing_skew, MENU_ENUM_SUBLABEL_AUDIO_MAX_TIMING_SKEW) default_sublabel_macro(action_bind_sublabel_pause_nonactive, MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE) @@ -1419,6 +1420,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_AUTO: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_refresh_rate_auto); break; + case MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_POLLED: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_refresh_rate_polled); + break; case MENU_ENUM_LABEL_VIDEO_MONITOR_INDEX: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_monitor_index); break; diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 3be1f16e85..864b8f06fa 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -163,6 +163,8 @@ int action_right_cheat(unsigned type, const char *label, int setting_action_ok_video_refresh_rate_auto(void *data, bool wraparound); +int setting_action_ok_video_refresh_rate_polled(void *data, bool wraparound); + int setting_action_ok_bind_all(void *data, bool wraparound); int setting_action_ok_bind_all_save_autoconfig(void *data, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 4a76ad04ac..229a2392ea 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5872,6 +5872,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_AUTO, PARSE_ONLY_FLOAT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_POLLED, + PARSE_ONLY_FLOAT, false); menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_FORCE_SRGB_DISABLE, PARSE_ONLY_BOOL, false); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 34dd201345..658f7d6764 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -1155,6 +1155,13 @@ static int setting_action_right_mouse_index(void *data, bool wraparound) ******* ACTION OK CALLBACK FUNCTIONS ******* **/ +static void +setting_get_string_representation_st_float_video_refresh_rate_polled( + void *data, char *s, size_t len) +{ + snprintf(s, len, "%.5f Hz", video_driver_get_refresh_rate()); +} + static void setting_get_string_representation_st_float_video_refresh_rate_auto( void *data, char *s, size_t len) @@ -1406,6 +1413,12 @@ void general_write_handler(void *data) case MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_AUTO: driver_ctl(RARCH_DRIVER_CTL_SET_REFRESH_RATE, setting->value.target.fraction); + /* In case refresh rate update forced non-block video. */ + rarch_cmd = CMD_EVENT_VIDEO_SET_BLOCKING_STATE; + break; + case MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_POLLED: + driver_ctl(RARCH_DRIVER_CTL_SET_REFRESH_RATE, setting->value.target.fraction); + /* In case refresh rate update forced non-block video. */ rarch_cmd = CMD_EVENT_VIDEO_SET_BLOCKING_STATE; break; @@ -3166,6 +3179,30 @@ static bool setting_append_list( &setting_get_string_representation_st_float_video_refresh_rate_auto; settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); + { + float actual_refresh_rate = video_driver_get_refresh_rate(); + if (actual_refresh_rate > 0.0) + { + CONFIG_FLOAT( + list, list_info, + &settings->floats.video_refresh_rate, + MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_POLLED, + MENU_ENUM_LABEL_VALUE_VIDEO_REFRESH_RATE_POLLED, + actual_refresh_rate, + "%.3f Hz", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_video_refresh_rate_polled; + (*list)[list_info->index - 1].action_select = &setting_action_ok_video_refresh_rate_polled; + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_st_float_video_refresh_rate_polled; + settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); + } + } + if (string_is_equal(settings->arrays.video_driver, "gl")) { CONFIG_BOOL( diff --git a/msg_hash.h b/msg_hash.h index 82ddf79a47..7070f56a4d 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -719,6 +719,7 @@ enum msg_hash_enums MENU_LABEL(VIDEO_WINDOW_SCALE), MENU_LABEL(VIDEO_REFRESH_RATE), MENU_LABEL(VIDEO_REFRESH_RATE_AUTO), + MENU_LABEL(VIDEO_REFRESH_RATE_POLLED), MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE, From e049605359e05ac9445edf5e72c33736f2930a36 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Sun, 15 Apr 2018 19:33:38 -0500 Subject: [PATCH 319/517] Add refresh rate polling to Win32 >= Windows 7. --- gfx/common/win32_common.c | 39 +++++++++++++++++++++++++++++++++++ gfx/common/win32_common.h | 2 ++ gfx/drivers/d3d10.c | 2 +- gfx/drivers/d3d11.c | 2 +- gfx/drivers/d3d12.c | 2 +- gfx/drivers/d3d8.c | 2 +- gfx/drivers/d3d9.c | 2 +- gfx/drivers/gdi_gfx.c | 2 +- gfx/drivers_context/wgl_ctx.c | 2 +- menu/menu_setting.c | 2 +- 10 files changed, 49 insertions(+), 8 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index fb46870faa..2cf6f9beb1 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -281,6 +281,45 @@ void win32_monitor_get_info(void) win32_change_display_settings(current_mon.szDevice, NULL, 0); } +float win32_get_refresh_rate(void *data) +{ + float refresh_rate = 0.0f; +#if _WIN32_WINNT >= 0x0601 || _WIN32_WINDOWS >= 0x0601 /* Win 7 */ + unsigned int NumPathArrayElements; + unsigned int NumModeInfoArrayElements; + DISPLAYCONFIG_PATH_INFO *PathInfoArray; + DISPLAYCONFIG_MODE_INFO *ModeInfoArray; + DISPLAYCONFIG_TOPOLOGY_ID TopologyID; + int result; + + GetDisplayConfigBufferSizes(QDC_DATABASE_CURRENT, + &NumPathArrayElements, + &NumModeInfoArrayElements); + + PathInfoArray = (DISPLAYCONFIG_PATH_INFO *) + malloc(sizeof (DISPLAYCONFIG_PATH_INFO) * NumPathArrayElements); + ModeInfoArray = (DISPLAYCONFIG_MODE_INFO *) + malloc(sizeof (DISPLAYCONFIG_MODE_INFO) * NumModeInfoArrayElements); + + result = QueryDisplayConfig(QDC_DATABASE_CURRENT, + &NumPathArrayElements, + PathInfoArray, + &NumModeInfoArrayElements, + ModeInfoArray, + &TopologyID); + if (result == ERROR_SUCCESS && NumPathArrayElements >= 1) + { + refresh_rate = (float) PathInfoArray[0].targetInfo.refreshRate.Numerator / + PathInfoArray[0].targetInfo.refreshRate.Denominator; + } + + free(ModeInfoArray); + free(PathInfoArray); + +#endif + return refresh_rate; +} + void win32_monitor_info(void *data, void *hm_data, unsigned *mon_id) { unsigned i; diff --git a/gfx/common/win32_common.h b/gfx/common/win32_common.h index 4220997fc2..40fa4d3b39 100644 --- a/gfx/common/win32_common.h +++ b/gfx/common/win32_common.h @@ -128,6 +128,8 @@ bool win32_taskbar_is_created(void); void win32_set_taskbar_created(bool created); +float win32_get_refresh_rate(void *data); + #if defined(HAVE_D3D8) || defined(HAVE_D3D9) || defined (HAVE_D3D10) || defined (HAVE_D3D11) || defined (HAVE_D3D12) LRESULT CALLBACK WndProcD3D(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index d455113097..214436e096 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -557,7 +557,7 @@ static const video_poke_interface_t d3d10_poke_interface = { NULL, /* load_texture */ NULL, /* unload_texture */ NULL, /* set_video_mode */ - NULL, /* get_refresh_rate */ + win32_get_refresh_rate, d3d10_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 6578f32df9..207b289796 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -1589,7 +1589,7 @@ static const video_poke_interface_t d3d11_poke_interface = { d3d11_gfx_load_texture, d3d11_gfx_unload_texture, NULL, /* set_video_mode */ - NULL, /* get_refresh_rate */ + win32_get_refresh_rate, d3d11_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index d7a8525f95..508cab852c 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -1752,7 +1752,7 @@ static const video_poke_interface_t d3d12_poke_interface = { d3d12_gfx_load_texture, d3d12_gfx_unload_texture, NULL, /* set_video_mode */ - NULL, /* get_refresh_rate */ + win32_get_refresh_rate, d3d12_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index ddce718f09..60e87e3a3d 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -1866,7 +1866,7 @@ static const video_poke_interface_t d3d_poke_interface = { d3d8_load_texture, d3d8_unload_texture, d3d8_set_video_mode, - NULL, + win32_get_refresh_rate, NULL, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index 85d1cc55db..b33ef22a45 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -1890,7 +1890,7 @@ static const video_poke_interface_t d3d9_poke_interface = { d3d9_load_texture, d3d9_unload_texture, d3d9_set_video_mode, - NULL, + win32_get_refresh_rate, NULL, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/gdi_gfx.c b/gfx/drivers/gdi_gfx.c index f09884bdfb..2d9f96d2e3 100644 --- a/gfx/drivers/gdi_gfx.c +++ b/gfx/drivers/gdi_gfx.c @@ -538,7 +538,7 @@ static const video_poke_interface_t gdi_poke_interface = { NULL, NULL, gdi_set_video_mode, - NULL, /* get_refresh_rate */ + win32_get_refresh_rate, NULL, gdi_get_video_output_size, gdi_get_video_output_prev, diff --git a/gfx/drivers_context/wgl_ctx.c b/gfx/drivers_context/wgl_ctx.c index a698eadfb3..47c5ad5a55 100644 --- a/gfx/drivers_context/wgl_ctx.c +++ b/gfx/drivers_context/wgl_ctx.c @@ -760,7 +760,7 @@ const gfx_ctx_driver_t gfx_ctx_wgl = { gfx_ctx_wgl_swap_interval, gfx_ctx_wgl_set_video_mode, gfx_ctx_wgl_get_video_size, - NULL, /* get_refresh_rate */ + win32_get_refresh_rate, gfx_ctx_wgl_get_video_output_size, gfx_ctx_wgl_get_video_output_prev, gfx_ctx_wgl_get_video_output_next, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 658f7d6764..65b5ae003f 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -1159,7 +1159,7 @@ static void setting_get_string_representation_st_float_video_refresh_rate_polled( void *data, char *s, size_t len) { - snprintf(s, len, "%.5f Hz", video_driver_get_refresh_rate()); + snprintf(s, len, "%.3f Hz", video_driver_get_refresh_rate()); } static void From df5528b1d0547c3c5b1223bca8e3c42ebdff52ce Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Sun, 15 Apr 2018 20:00:14 -0500 Subject: [PATCH 320/517] Divide Wayland rate by 1000 instead of multiply. --- gfx/drivers_context/wayland_ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/drivers_context/wayland_ctx.c b/gfx/drivers_context/wayland_ctx.c index 07e1d7b3aa..50ebe36e06 100644 --- a/gfx/drivers_context/wayland_ctx.c +++ b/gfx/drivers_context/wayland_ctx.c @@ -1375,7 +1375,7 @@ static float gfx_ctx_wl_get_refresh_rate(void *data) { gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; - return (float) wl->refresh_rate * 1000.0f; + return (float) wl->refresh_rate / 1000.0f; } const gfx_ctx_driver_t gfx_ctx_wayland = { From 4cc3cf73446964864ef55990706dc46319354a2f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 16 Apr 2018 03:19:33 +0200 Subject: [PATCH 321/517] Only restore resolution through resolution switch if CRT switch mode was ever actually used --- gfx/video_driver.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 60f475de59..9d2a9b6d40 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -144,6 +144,7 @@ static void *video_driver_state_buffer = NULL; static unsigned video_driver_state_scale = 0; static unsigned video_driver_state_out_bpp = 0; static bool video_driver_state_out_rgb32 = false; +static bool video_driver_crt_switched = false; static bool video_driver_crt_switching_active = false; static struct retro_system_av_info video_driver_av_info; @@ -1571,7 +1572,13 @@ static void video_driver_lock_new(void) void video_driver_destroy(void) { video_display_server_destroy(); - crt_video_restore(); + + if (video_driver_crt_switched) + { + crt_video_restore(); + video_driver_crt_switched = false; + } + video_driver_cb_has_focus = null_driver_has_focus; video_driver_use_rgba = false; video_driver_data_own = false; @@ -2599,6 +2606,7 @@ void video_driver_frame(const void *data, unsigned width, /* trigger set resolution*/ if (video_info.crt_switch_resolution) { + video_driver_crt_switched = true; video_driver_crt_switching_active = true; if (video_info.crt_switch_resolution_super == 2560) From d46011ae14ba62734e3903511258de8df43f5772 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 16 Apr 2018 04:20:24 +0200 Subject: [PATCH 322/517] Cleanups --- gfx/common/win32_common.c | 155 ++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 81 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index 2cf6f9beb1..d9816ea3ac 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -83,18 +83,22 @@ extern void *dinput_wgl; extern void *dinput; #endif -unsigned g_resize_width = 0; -unsigned g_resize_height = 0; static bool g_resized = false; bool g_restore_desktop = false; static bool doubleclick_on_titlebar = false; +static bool g_taskbar_is_created = false; bool g_inited = false; static bool g_quit = false; + static int g_pos_x = CW_USEDEFAULT; static int g_pos_y = CW_USEDEFAULT; -static void *curD3D = NULL; -static bool g_taskbar_is_created = false; + +unsigned g_resize_width = 0; +unsigned g_resize_height = 0; static unsigned g_taskbar_message = 0; +static unsigned win32_monitor_count = 0; + +static void *curD3D = NULL; ui_window_win32_t main_window; @@ -147,7 +151,6 @@ typedef REASON_CONTEXT POWER_REQUEST_CONTEXT, *PPOWER_REQUEST_CONTEXT, *LPPOWER_ static HMONITOR win32_monitor_last; static HMONITOR win32_monitor_all[MAX_MONITORS]; -static unsigned win32_monitor_count = 0; bool win32_taskbar_is_created(void) { @@ -283,14 +286,14 @@ void win32_monitor_get_info(void) float win32_get_refresh_rate(void *data) { - float refresh_rate = 0.0f; + float refresh_rate = 0.0f; #if _WIN32_WINNT >= 0x0601 || _WIN32_WINDOWS >= 0x0601 /* Win 7 */ - unsigned int NumPathArrayElements; - unsigned int NumModeInfoArrayElements; - DISPLAYCONFIG_PATH_INFO *PathInfoArray; - DISPLAYCONFIG_MODE_INFO *ModeInfoArray; DISPLAYCONFIG_TOPOLOGY_ID TopologyID; - int result; + unsigned int NumPathArrayElements = 0; + unsigned int NumModeInfoArrayElements = 0; + DISPLAYCONFIG_PATH_INFO *PathInfoArray = NULL; + DISPLAYCONFIG_MODE_INFO *ModeInfoArray = NULL; + int result = 0; GetDisplayConfigBufferSizes(QDC_DATABASE_CURRENT, &NumPathArrayElements, @@ -419,10 +422,10 @@ static int win32_drag_query_file(HWND hwnd, WPARAM wparam) { const core_info_t *info = (const core_info_t*)&core_info[i]; - if(!string_is_equal(info->systemname, current_core->systemname)) + if (!string_is_equal(info->systemname, current_core->systemname)) break; - if(string_is_equal(path_get(RARCH_PATH_CORE), info->path)) + if (string_is_equal(path_get(RARCH_PATH_CORE), info->path)) { /* Our previous core supports the current rom */ content_ctx_info_t content_info = {0}; @@ -437,29 +440,22 @@ static int win32_drag_query_file(HWND hwnd, WPARAM wparam) } /* Poll for cores for current rom since none exist. */ - if(list_size ==1) + if (list_size ==1) { /*pick core that only exists and is bound to work. Ish. */ const core_info_t *info = (const core_info_t*)&core_info[0]; if (info) task_push_load_content_with_new_core_from_companion_ui( - info->path, NULL, - &content_info, - NULL, NULL); + info->path, NULL, &content_info, NULL, NULL); } else { /* Pick one core that could be compatible, ew */ - if(DialogBoxParam(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_PICKCORE), + if (DialogBoxParam(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_PICKCORE), hwnd,PickCoreProc,(LPARAM)NULL)==IDOK) - { task_push_load_content_with_current_core_from_companion_ui( - NULL, - &content_info, - CORE_TYPE_PLAIN, - NULL, NULL); - } + NULL, &content_info, CORE_TYPE_PLAIN, NULL, NULL); } } @@ -470,10 +466,7 @@ static int win32_drag_query_file(HWND hwnd, WPARAM wparam) static LRESULT win32_handle_keyboard_event(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { - unsigned keycode = 0; uint16_t mod = 0; - bool keydown = true; - settings_t *settings = NULL; if (GetKeyState(VK_SHIFT) & 0x80) mod |= RETROKMOD_SHIFT; @@ -494,7 +487,7 @@ static LRESULT win32_handle_keyboard_event(HWND hwnd, UINT message, * WM_CHAR and WM_KEYDOWN properly. */ case WM_CHAR: - input_keyboard_event(keydown, RETROK_UNKNOWN, wparam, mod, + input_keyboard_event(true, RETROK_UNKNOWN, wparam, mod, RETRO_DEVICE_KEYBOARD); return TRUE; @@ -502,35 +495,35 @@ static LRESULT win32_handle_keyboard_event(HWND hwnd, UINT message, case WM_SYSKEYUP: case WM_KEYDOWN: case WM_SYSKEYDOWN: - /* Key released? */ - if (message == WM_KEYUP || message == WM_SYSKEYUP) - keydown = false; - -#if _WIN32_WINNT >= 0x0501 /* XP */ - settings = config_get_ptr(); - if (settings && string_is_equal(settings->arrays.input_driver, "raw")) - keycode = input_keymaps_translate_keysym_to_rk((unsigned)(wparam)); - else -#endif - keycode = input_keymaps_translate_keysym_to_rk((lparam >> 16) & 0xff); - - input_keyboard_event(keydown, keycode, 0, mod, RETRO_DEVICE_KEYBOARD); - - if (message == WM_SYSKEYDOWN) { - switch (wparam) - { - case VK_F10: - case VK_MENU: - case VK_RSHIFT: - return 0; - default: - break; - } - } - else - return 0; + unsigned keycode = 0; + bool keydown = true; + unsigned keysym = (lparam >> 16) & 0xff; +#if _WIN32_WINNT >= 0x0501 /* XP */ + settings_t *settings = config_get_ptr(); + if (settings && string_is_equal(settings->arrays.input_driver, "raw")) + keysym = (unsigned)wparam; +#endif + /* Key released? */ + if (message == WM_KEYUP || message == WM_SYSKEYUP) + keydown = false; + + keycode = input_keymaps_translate_keysym_to_rk(keysym); + + input_keyboard_event(keydown, keycode, + 0, mod, RETRO_DEVICE_KEYBOARD); + + if (message != WM_SYSKEYDOWN) + return 0; + + if ( + wparam == VK_F10 || + wparam == VK_MENU || + wparam == VK_RSHIFT + ) + return 0; + } break; } @@ -590,7 +583,8 @@ static LRESULT CALLBACK WndProcCommon(bool *quit, HWND hwnd, UINT message, break; case WM_SIZE: /* Do not send resize message if we minimize. */ - if (wparam != SIZE_MAXHIDE && wparam != SIZE_MINIMIZED) + if ( wparam != SIZE_MAXHIDE && + wparam != SIZE_MINIMIZED) { g_resize_width = LOWORD(lparam); g_resize_height = HIWORD(lparam); @@ -886,9 +880,7 @@ bool win32_window_create(void *data, unsigned style, bool win32_get_metrics(void *data, enum display_metric_types type, float *value) { -#ifdef _XBOX - return false; -#else +#ifndef _XBOX HDC monitor = GetDC(NULL); int pixels_x = GetDeviceCaps(monitor, HORZRES); int pixels_y = GetDeviceCaps(monitor, VERTRES); @@ -901,22 +893,22 @@ bool win32_get_metrics(void *data, { case DISPLAY_METRIC_MM_WIDTH: *value = physical_width; - break; + return true; case DISPLAY_METRIC_MM_HEIGHT: *value = physical_height; - break; + return true; case DISPLAY_METRIC_DPI: /* 25.4 mm in an inch. */ *value = 254 * pixels_x / physical_width / 10; - break; + return true; case DISPLAY_METRIC_NONE: default: *value = 0; - return false; + break; } - - return true; #endif + + return false; } void win32_monitor_init(void) @@ -986,7 +978,7 @@ void win32_check_window(bool *quit, bool *resize, bool win32_suppress_screensaver(void *data, bool enable) { #ifndef _XBOX - if(enable) + if (enable) { char tmp[PATH_MAX_LENGTH]; int major = 0; @@ -1012,16 +1004,20 @@ bool win32_suppress_screensaver(void *data, bool enable) PowerSetRequestPtr powerSetRequest = (PowerSetRequestPtr)GetProcAddress(kernel32, "PowerSetRequest"); - if(powerCreateRequest && powerSetRequest) + if (powerCreateRequest && powerSetRequest) { POWER_REQUEST_CONTEXT RequestContext; HANDLE Request; - RequestContext.Version = POWER_REQUEST_CONTEXT_VERSION; - RequestContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; - RequestContext.Reason.SimpleReasonString = (LPWSTR)L"RetroArch running"; + RequestContext.Version = + POWER_REQUEST_CONTEXT_VERSION; + RequestContext.Flags = + POWER_REQUEST_CONTEXT_SIMPLE_STRING; + RequestContext.Reason.SimpleReasonString = (LPWSTR) + L"RetroArch running"; - Request = powerCreateRequest(&RequestContext); + Request = + powerCreateRequest(&RequestContext); powerSetRequest( Request, PowerRequestDisplayRequired); return true; @@ -1075,8 +1071,7 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, *style = WS_POPUP | WS_VISIBLE; if (!win32_monitor_set_fullscreen(*width, *height, - refresh, current_mon->szDevice)) - {} + refresh, current_mon->szDevice)) { } /* Display settings might have changed, get new coordinates. */ GetMonitorInfo(*hm_to_use, (LPMONITORINFO)current_mon); @@ -1184,11 +1179,9 @@ bool win32_set_video_mode(void *data, RARCH_ERR("GetMessage error code %d\n", GetLastError()); break; } - else - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + + TranslateMessage(&msg); + DispatchMessage(&msg); } if (g_quit) @@ -1280,7 +1273,7 @@ void win32_get_video_output_prev( EnumDisplaySettings(NULL, i, &dm) != 0; i++) { - if ( dm.dmPelsWidth == curr_width + if ( dm.dmPelsWidth == curr_width && dm.dmPelsHeight == curr_height) { if ( prev_width != curr_width @@ -1327,7 +1320,7 @@ void win32_get_video_output_next( break; } - if ( dm.dmPelsWidth == curr_width + if ( dm.dmPelsWidth == curr_width && dm.dmPelsHeight == curr_height) found = true; } @@ -1337,7 +1330,7 @@ void win32_get_video_output_size(unsigned *width, unsigned *height) { DEVMODE dm; memset(&dm, 0, sizeof(dm)); - dm.dmSize = sizeof(dm); + dm.dmSize = sizeof(dm); if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm) != 0) { From ec2267daa6fc07eb9acd38037c70d0ddf94c6045 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 16 Apr 2018 04:35:23 +0200 Subject: [PATCH 323/517] Cleanups --- gfx/video_crt_switch.c | 8 +++++++- gfx/video_driver.c | 9 +-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index 874e5c3afa..3b906fcdf3 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -55,7 +55,8 @@ static void crt_check_first_run(void) orig_height = GetSystemMetrics(SM_CYSCREEN); orig_width = GetSystemMetrics(SM_CXSCREEN); #endif - first_run = false; + + first_run = false; } static void switch_crt_hz(void) @@ -185,6 +186,11 @@ void crt_switch_res_core(unsigned width, unsigned height, float hz) void crt_video_restore(void) { + if (first_run) + return; + video_display_server_switch_resolution(orig_width, orig_height, 0, 60); + + first_run = true; } diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 9d2a9b6d40..f18fd34b71 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -144,7 +144,6 @@ static void *video_driver_state_buffer = NULL; static unsigned video_driver_state_scale = 0; static unsigned video_driver_state_out_bpp = 0; static bool video_driver_state_out_rgb32 = false; -static bool video_driver_crt_switched = false; static bool video_driver_crt_switching_active = false; static struct retro_system_av_info video_driver_av_info; @@ -1572,12 +1571,7 @@ static void video_driver_lock_new(void) void video_driver_destroy(void) { video_display_server_destroy(); - - if (video_driver_crt_switched) - { - crt_video_restore(); - video_driver_crt_switched = false; - } + crt_video_restore(); video_driver_cb_has_focus = null_driver_has_focus; video_driver_use_rgba = false; @@ -2606,7 +2600,6 @@ void video_driver_frame(const void *data, unsigned width, /* trigger set resolution*/ if (video_info.crt_switch_resolution) { - video_driver_crt_switched = true; video_driver_crt_switching_active = true; if (video_info.crt_switch_resolution_super == 2560) From 7a4c468730be0209ceb637ce96b57a841cf428d9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 16 Apr 2018 08:34:34 +0200 Subject: [PATCH 324/517] Update libretro-common --- libretro-common/compat/compat_snprintf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libretro-common/compat/compat_snprintf.c b/libretro-common/compat/compat_snprintf.c index 40734a6fe2..00336a0deb 100644 --- a/libretro-common/compat/compat_snprintf.c +++ b/libretro-common/compat/compat_snprintf.c @@ -24,8 +24,9 @@ #ifdef _MSC_VER #include - +#if _MSC_VER >= 1900 #include /* added for _vsnprintf_s and _vscprintf on VS2015 and VS2017 */ +#endif #include #if _MSC_VER < 1800 From be7eb4de1cda19b82f00fdfebfa3a914a5492715 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 16 Apr 2018 08:57:17 +0200 Subject: [PATCH 325/517] Update libretro-common --- libretro-common/audio/audio_mix.c | 2 +- libretro-common/audio/audio_mixer.c | 2 +- .../audio/conversion/float_to_s16.c | 2 +- .../audio/conversion/float_to_s16_neon.S | 2 +- .../audio/conversion/float_to_s16_neon.c | 2 +- .../audio/conversion/s16_to_float.c | 2 +- .../audio/conversion/s16_to_float_neon.S | 2 +- .../audio/conversion/s16_to_float_neon.c | 2 +- libretro-common/audio/dsp_filter.c | 2 +- libretro-common/audio/dsp_filters/fft/fft.c | 2 +- libretro-common/audio/dsp_filters/fft/fft.h | 2 +- .../audio/resampler/audio_resampler.c | 2 +- .../resampler/drivers/nearest_resampler.c | 2 +- .../audio/resampler/drivers/null_resampler.c | 2 +- .../audio/resampler/drivers/sinc_resampler.c | 2 +- .../resampler/drivers/sinc_resampler_neon.S | 2 +- libretro-common/compat/compat_fnmatch.c | 2 +- libretro-common/compat/compat_getopt.c | 2 +- libretro-common/compat/compat_posix_string.c | 2 +- libretro-common/compat/compat_snprintf.c | 2 +- libretro-common/compat/compat_strcasestr.c | 2 +- libretro-common/compat/compat_strl.c | 2 +- libretro-common/compat/fopen_utf8.c | 22 +++++++++++++++ libretro-common/dynamic/dylib.c | 2 +- libretro-common/encodings/encoding_crc32.c | 2 +- libretro-common/encodings/encoding_utf.c | 2 +- libretro-common/features/features_cpu.c | 2 +- libretro-common/file/archive_file.c | 2 +- libretro-common/file/archive_file_7z.c | 2 +- libretro-common/file/archive_file_zlib.c | 2 +- libretro-common/file/config_file.c | 2 +- libretro-common/file/config_file_userdata.c | 2 +- libretro-common/file/file_path.c | 2 +- libretro-common/file/nbio/nbio_intf.c | 2 +- libretro-common/file/nbio/nbio_linux.c | 2 +- libretro-common/file/nbio/nbio_stdio.c | 2 +- libretro-common/file/nbio/nbio_unixmmap.c | 2 +- libretro-common/file/nbio/nbio_windowsmmap.c | 2 +- libretro-common/file/retro_dirent.c | 2 +- libretro-common/formats/bmp/rbmp.c | 2 +- libretro-common/formats/bmp/rbmp_encode.c | 2 +- libretro-common/formats/image_texture.c | 2 +- libretro-common/formats/image_transfer.c | 2 +- libretro-common/formats/jpeg/rjpeg.c | 2 +- libretro-common/formats/json/jsonsax.c | 2 +- libretro-common/formats/png/rpng.c | 2 +- libretro-common/formats/png/rpng_encode.c | 2 +- libretro-common/formats/png/rpng_internal.h | 2 +- libretro-common/formats/tga/rtga.c | 2 +- libretro-common/formats/wav/rwav.c | 2 +- libretro-common/formats/xml/rxml.c | 2 +- libretro-common/formats/xml/test/rxml_test.c | 2 +- libretro-common/gfx/gl_capabilities.c | 2 +- libretro-common/gfx/scaler/pixconv.c | 2 +- libretro-common/gfx/scaler/scaler.c | 2 +- libretro-common/gfx/scaler/scaler_filter.c | 2 +- libretro-common/gfx/scaler/scaler_int.c | 2 +- libretro-common/glsm/glsm.c | 2 +- libretro-common/glsym/glsym_gl.c | 2 +- libretro-common/glsym/rglgen.c | 2 +- libretro-common/hash/rhash.c | 2 +- libretro-common/include/audio/audio_mix.h | 2 +- libretro-common/include/audio/audio_mixer.h | 2 +- .../include/audio/audio_resampler.h | 2 +- .../include/audio/conversion/float_to_s16.h | 2 +- .../include/audio/conversion/s16_to_float.h | 2 +- libretro-common/include/audio/dsp_filter.h | 2 +- libretro-common/include/boolean.h | 2 +- libretro-common/include/clamping.h | 2 +- libretro-common/include/compat/apple_compat.h | 2 +- libretro-common/include/compat/fnmatch.h | 2 +- libretro-common/include/compat/fopen_utf8.h | 28 +++++++++++++++++-- libretro-common/include/compat/getopt.h | 2 +- libretro-common/include/compat/intrinsics.h | 2 +- libretro-common/include/compat/msvc.h | 2 +- libretro-common/include/compat/posix_string.h | 2 +- libretro-common/include/compat/strcasestr.h | 2 +- libretro-common/include/compat/strl.h | 2 +- libretro-common/include/dynamic/dylib.h | 2 +- libretro-common/include/encodings/crc32.h | 2 +- libretro-common/include/encodings/utf.h | 2 +- libretro-common/include/encodings/win32.h | 2 +- libretro-common/include/fastcpy.h | 2 +- .../include/features/features_cpu.h | 2 +- libretro-common/include/file/archive_file.h | 2 +- libretro-common/include/file/config_file.h | 2 +- .../include/file/config_file_userdata.h | 2 +- libretro-common/include/file/file_path.h | 2 +- libretro-common/include/file/nbio.h | 2 +- libretro-common/include/filters.h | 2 +- libretro-common/include/formats/image.h | 2 +- libretro-common/include/formats/jsonsax.h | 2 +- libretro-common/include/formats/rbmp.h | 2 +- libretro-common/include/formats/rjpeg.h | 2 +- libretro-common/include/formats/rpng.h | 2 +- libretro-common/include/formats/rtga.h | 2 +- libretro-common/include/formats/rwav.h | 2 +- libretro-common/include/formats/rxml.h | 2 +- libretro-common/include/gfx/gl_capabilities.h | 2 +- libretro-common/include/gfx/math/matrix_3x3.h | 2 +- libretro-common/include/gfx/math/matrix_4x4.h | 2 +- libretro-common/include/gfx/math/vector_2.h | 2 +- libretro-common/include/gfx/math/vector_3.h | 2 +- libretro-common/include/gfx/math/vector_4.h | 2 +- libretro-common/include/gfx/scaler/filter.h | 2 +- libretro-common/include/gfx/scaler/pixconv.h | 2 +- libretro-common/include/gfx/scaler/scaler.h | 2 +- .../include/gfx/scaler/scaler_int.h | 2 +- libretro-common/include/gfx/video_frame.h | 2 +- libretro-common/include/glsm/glsm.h | 2 +- libretro-common/include/glsm/glsmsym.h | 2 +- libretro-common/include/glsym/glsym.h | 2 +- libretro-common/include/glsym/glsym_gl.h | 2 +- libretro-common/include/glsym/rglgen.h | 2 +- .../include/glsym/rglgen_headers.h | 2 +- libretro-common/include/libco.h | 2 +- libretro-common/include/libretro.h | 2 +- libretro-common/include/libretro_d3d.h | 2 +- libretro-common/include/libretro_dspfilter.h | 2 +- libretro-common/include/libretro_vulkan.h | 2 +- libretro-common/include/lists/dir_list.h | 2 +- libretro-common/include/lists/file_list.h | 2 +- libretro-common/include/lists/string_list.h | 2 +- libretro-common/include/math/complex.h | 2 +- libretro-common/include/math/float_minmax.h | 2 +- libretro-common/include/math/fxp.h | 2 +- libretro-common/include/memalign.h | 2 +- libretro-common/include/memmap.h | 2 +- libretro-common/include/net/net_compat.h | 2 +- libretro-common/include/net/net_http.h | 2 +- libretro-common/include/net/net_http_parse.h | 2 +- libretro-common/include/net/net_ifinfo.h | 2 +- libretro-common/include/net/net_natt.h | 2 +- libretro-common/include/net/net_socket.h | 2 +- libretro-common/include/net/net_socket_ssl.h | 2 +- libretro-common/include/queues/fifo_queue.h | 2 +- .../include/queues/message_queue.h | 2 +- libretro-common/include/queues/task_queue.h | 2 +- libretro-common/include/retro_assert.h | 2 +- libretro-common/include/retro_common.h | 2 +- libretro-common/include/retro_common_api.h | 2 +- libretro-common/include/retro_dirent.h | 2 +- libretro-common/include/retro_endianness.h | 2 +- libretro-common/include/retro_environment.h | 2 +- libretro-common/include/retro_inline.h | 2 +- libretro-common/include/retro_math.h | 2 +- libretro-common/include/retro_miscellaneous.h | 2 +- libretro-common/include/retro_timers.h | 2 +- libretro-common/include/rhash.h | 2 +- libretro-common/include/rthreads/rthreads.h | 2 +- libretro-common/include/streams/chd_stream.h | 2 +- libretro-common/include/streams/file_stream.h | 2 +- .../include/streams/file_stream_transforms.h | 2 +- .../include/streams/interface_stream.h | 2 +- .../include/streams/memory_stream.h | 2 +- .../include/streams/stdin_stream.h | 2 +- .../include/streams/trans_stream.h | 2 +- libretro-common/include/string/stdstring.h | 2 +- .../include/vfs/vfs_implementation.h | 2 +- libretro-common/lists/dir_list.c | 2 +- libretro-common/lists/file_list.c | 2 +- libretro-common/lists/string_list.c | 2 +- libretro-common/lists/vector_list.c | 2 +- libretro-common/memmap/memalign.c | 2 +- libretro-common/memmap/memmap.c | 2 +- libretro-common/net/net_compat.c | 2 +- libretro-common/net/net_http.c | 2 +- libretro-common/net/net_http_parse.c | 2 +- libretro-common/net/net_ifinfo.c | 2 +- libretro-common/net/net_natt.c | 2 +- libretro-common/net/net_socket.c | 2 +- libretro-common/net/net_socket_ssl.c | 2 +- libretro-common/queues/fifo_queue.c | 2 +- libretro-common/queues/message_queue.c | 2 +- libretro-common/queues/task_queue.c | 2 +- libretro-common/rthreads/ctr_pthread.h | 2 +- libretro-common/rthreads/gx_pthread.h | 2 +- libretro-common/rthreads/rthreads.c | 2 +- libretro-common/rthreads/xenon_sdl_threads.c | 2 +- libretro-common/streams/chd_stream.c | 2 +- libretro-common/streams/file_stream.c | 2 +- .../streams/file_stream_transforms.c | 2 +- libretro-common/streams/interface_stream.c | 2 +- libretro-common/streams/memory_stream.c | 2 +- libretro-common/streams/stdin_stream.c | 2 +- libretro-common/streams/trans_stream.c | 2 +- libretro-common/streams/trans_stream_pipe.c | 2 +- libretro-common/streams/trans_stream_zlib.c | 2 +- libretro-common/string/stdstring.c | 2 +- libretro-common/vfs/vfs_implementation.c | 2 +- 190 files changed, 235 insertions(+), 191 deletions(-) diff --git a/libretro-common/audio/audio_mix.c b/libretro-common/audio/audio_mix.c index bdf345a211..15879ab248 100644 --- a/libretro-common/audio/audio_mix.c +++ b/libretro-common/audio/audio_mix.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (audio_mix.c). diff --git a/libretro-common/audio/audio_mixer.c b/libretro-common/audio/audio_mixer.c index 0984fc2d87..4dffa95c84 100644 --- a/libretro-common/audio/audio_mixer.c +++ b/libretro-common/audio/audio_mixer.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (audio_mixer.c). diff --git a/libretro-common/audio/conversion/float_to_s16.c b/libretro-common/audio/conversion/float_to_s16.c index bb97945971..baf8b4fd01 100644 --- a/libretro-common/audio/conversion/float_to_s16.c +++ b/libretro-common/audio/conversion/float_to_s16.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (float_to_s16.c). diff --git a/libretro-common/audio/conversion/float_to_s16_neon.S b/libretro-common/audio/conversion/float_to_s16_neon.S index aa9e565671..352c4d3dc7 100644 --- a/libretro-common/audio/conversion/float_to_s16_neon.S +++ b/libretro-common/audio/conversion/float_to_s16_neon.S @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (float_to_s16_neon.S). diff --git a/libretro-common/audio/conversion/float_to_s16_neon.c b/libretro-common/audio/conversion/float_to_s16_neon.c index 342f4978e1..a5d10e0a68 100644 --- a/libretro-common/audio/conversion/float_to_s16_neon.c +++ b/libretro-common/audio/conversion/float_to_s16_neon.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (float_to_s16_neon.S). diff --git a/libretro-common/audio/conversion/s16_to_float.c b/libretro-common/audio/conversion/s16_to_float.c index 814ddd8261..c8042ea817 100644 --- a/libretro-common/audio/conversion/s16_to_float.c +++ b/libretro-common/audio/conversion/s16_to_float.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (s16_to_float.c). diff --git a/libretro-common/audio/conversion/s16_to_float_neon.S b/libretro-common/audio/conversion/s16_to_float_neon.S index b744924400..3cc47e9f8f 100644 --- a/libretro-common/audio/conversion/s16_to_float_neon.S +++ b/libretro-common/audio/conversion/s16_to_float_neon.S @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (s16_to_float_neon.S). diff --git a/libretro-common/audio/conversion/s16_to_float_neon.c b/libretro-common/audio/conversion/s16_to_float_neon.c index 464e40fbc9..9912c0d50b 100644 --- a/libretro-common/audio/conversion/s16_to_float_neon.c +++ b/libretro-common/audio/conversion/s16_to_float_neon.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (s16_to_float_neon.S). diff --git a/libretro-common/audio/dsp_filter.c b/libretro-common/audio/dsp_filter.c index 7f428eee12..c7bb2dc9c9 100644 --- a/libretro-common/audio/dsp_filter.c +++ b/libretro-common/audio/dsp_filter.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (dsp_filter.c). diff --git a/libretro-common/audio/dsp_filters/fft/fft.c b/libretro-common/audio/dsp_filters/fft/fft.c index e8c80f4f9d..7bafefad1e 100644 --- a/libretro-common/audio/dsp_filters/fft/fft.c +++ b/libretro-common/audio/dsp_filters/fft/fft.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (fft.c). diff --git a/libretro-common/audio/dsp_filters/fft/fft.h b/libretro-common/audio/dsp_filters/fft/fft.h index 8a16c6cb83..e5bcdf7176 100644 --- a/libretro-common/audio/dsp_filters/fft/fft.h +++ b/libretro-common/audio/dsp_filters/fft/fft.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (fft.h). diff --git a/libretro-common/audio/resampler/audio_resampler.c b/libretro-common/audio/resampler/audio_resampler.c index 3cf4660f47..8d27f06932 100644 --- a/libretro-common/audio/resampler/audio_resampler.c +++ b/libretro-common/audio/resampler/audio_resampler.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (audio_resampler.c). diff --git a/libretro-common/audio/resampler/drivers/nearest_resampler.c b/libretro-common/audio/resampler/drivers/nearest_resampler.c index 1083c85836..982d4fe67d 100644 --- a/libretro-common/audio/resampler/drivers/nearest_resampler.c +++ b/libretro-common/audio/resampler/drivers/nearest_resampler.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (nearest_resampler.c). diff --git a/libretro-common/audio/resampler/drivers/null_resampler.c b/libretro-common/audio/resampler/drivers/null_resampler.c index e75026944f..b970dfe0f5 100644 --- a/libretro-common/audio/resampler/drivers/null_resampler.c +++ b/libretro-common/audio/resampler/drivers/null_resampler.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (null_resampler.c). diff --git a/libretro-common/audio/resampler/drivers/sinc_resampler.c b/libretro-common/audio/resampler/drivers/sinc_resampler.c index b5754f6632..c400ab1b23 100644 --- a/libretro-common/audio/resampler/drivers/sinc_resampler.c +++ b/libretro-common/audio/resampler/drivers/sinc_resampler.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (sinc_resampler.c). diff --git a/libretro-common/audio/resampler/drivers/sinc_resampler_neon.S b/libretro-common/audio/resampler/drivers/sinc_resampler_neon.S index 033104e064..15a3773414 100644 --- a/libretro-common/audio/resampler/drivers/sinc_resampler_neon.S +++ b/libretro-common/audio/resampler/drivers/sinc_resampler_neon.S @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (sinc_resampler_neon.S). diff --git a/libretro-common/compat/compat_fnmatch.c b/libretro-common/compat/compat_fnmatch.c index 81beb3e06b..19f87cc76a 100644 --- a/libretro-common/compat/compat_fnmatch.c +++ b/libretro-common/compat/compat_fnmatch.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (compat_fnmatch.c). diff --git a/libretro-common/compat/compat_getopt.c b/libretro-common/compat/compat_getopt.c index 632f67e9f4..81978b86c2 100644 --- a/libretro-common/compat/compat_getopt.c +++ b/libretro-common/compat/compat_getopt.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (compat_getopt.c). diff --git a/libretro-common/compat/compat_posix_string.c b/libretro-common/compat/compat_posix_string.c index 4bddf11f8f..33a30e5772 100644 --- a/libretro-common/compat/compat_posix_string.c +++ b/libretro-common/compat/compat_posix_string.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (compat_posix_string.c). diff --git a/libretro-common/compat/compat_snprintf.c b/libretro-common/compat/compat_snprintf.c index 00336a0deb..074cf8e1bf 100644 --- a/libretro-common/compat/compat_snprintf.c +++ b/libretro-common/compat/compat_snprintf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (compat_snprintf.c). diff --git a/libretro-common/compat/compat_strcasestr.c b/libretro-common/compat/compat_strcasestr.c index 82ce5acb7b..54c93a48d0 100644 --- a/libretro-common/compat/compat_strcasestr.c +++ b/libretro-common/compat/compat_strcasestr.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (compat_strcasestr.c). diff --git a/libretro-common/compat/compat_strl.c b/libretro-common/compat/compat_strl.c index d42fca3655..94cb39b62b 100644 --- a/libretro-common/compat/compat_strl.c +++ b/libretro-common/compat/compat_strl.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (compat_strl.c). diff --git a/libretro-common/compat/fopen_utf8.c b/libretro-common/compat/fopen_utf8.c index d9fe35fec6..52b481e7e7 100644 --- a/libretro-common/compat/fopen_utf8.c +++ b/libretro-common/compat/fopen_utf8.c @@ -1,3 +1,25 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (fopen_utf8.c). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + #include #include #include diff --git a/libretro-common/dynamic/dylib.c b/libretro-common/dynamic/dylib.c index d4864a7057..bc6be9fc40 100644 --- a/libretro-common/dynamic/dylib.c +++ b/libretro-common/dynamic/dylib.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (dylib.c). diff --git a/libretro-common/encodings/encoding_crc32.c b/libretro-common/encodings/encoding_crc32.c index 0c1895a98f..4775e76357 100644 --- a/libretro-common/encodings/encoding_crc32.c +++ b/libretro-common/encodings/encoding_crc32.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (encoding_crc32.c). diff --git a/libretro-common/encodings/encoding_utf.c b/libretro-common/encodings/encoding_utf.c index d6c4ff4650..f7c533f144 100644 --- a/libretro-common/encodings/encoding_utf.c +++ b/libretro-common/encodings/encoding_utf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (encoding_utf.c). diff --git a/libretro-common/features/features_cpu.c b/libretro-common/features/features_cpu.c index dc3872043f..3a32a46c64 100644 --- a/libretro-common/features/features_cpu.c +++ b/libretro-common/features/features_cpu.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (features_cpu.c). diff --git a/libretro-common/file/archive_file.c b/libretro-common/file/archive_file.c index 4a3c387092..b2f35518f4 100644 --- a/libretro-common/file/archive_file.c +++ b/libretro-common/file/archive_file.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (archive_file.c). diff --git a/libretro-common/file/archive_file_7z.c b/libretro-common/file/archive_file_7z.c index 1afd5d4b83..0f00c0723d 100644 --- a/libretro-common/file/archive_file_7z.c +++ b/libretro-common/file/archive_file_7z.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (archive_file_sevenzip.c). diff --git a/libretro-common/file/archive_file_zlib.c b/libretro-common/file/archive_file_zlib.c index 4716ba95a3..3870dacaed 100644 --- a/libretro-common/file/archive_file_zlib.c +++ b/libretro-common/file/archive_file_zlib.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (archive_file_zlib.c). diff --git a/libretro-common/file/config_file.c b/libretro-common/file/config_file.c index 50bdfd8e30..a8f8cfa808 100644 --- a/libretro-common/file/config_file.c +++ b/libretro-common/file/config_file.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (config_file.c). diff --git a/libretro-common/file/config_file_userdata.c b/libretro-common/file/config_file_userdata.c index 8490fda4ad..e91fb8b04e 100644 --- a/libretro-common/file/config_file_userdata.c +++ b/libretro-common/file/config_file_userdata.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (config_file_userdata.c). diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index 880153ffc8..0b4c4b1244 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (file_path.c). diff --git a/libretro-common/file/nbio/nbio_intf.c b/libretro-common/file/nbio/nbio_intf.c index cfcef60d2b..d6254ef08e 100644 --- a/libretro-common/file/nbio/nbio_intf.c +++ b/libretro-common/file/nbio/nbio_intf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (nbio_intf.c). diff --git a/libretro-common/file/nbio/nbio_linux.c b/libretro-common/file/nbio/nbio_linux.c index 8ef3f2bacb..8644bb94cb 100644 --- a/libretro-common/file/nbio/nbio_linux.c +++ b/libretro-common/file/nbio/nbio_linux.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (nbio_linux.c). diff --git a/libretro-common/file/nbio/nbio_stdio.c b/libretro-common/file/nbio/nbio_stdio.c index aa9d3cc522..f80e91a27b 100644 --- a/libretro-common/file/nbio/nbio_stdio.c +++ b/libretro-common/file/nbio/nbio_stdio.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (nbio_stdio.c). diff --git a/libretro-common/file/nbio/nbio_unixmmap.c b/libretro-common/file/nbio/nbio_unixmmap.c index 672b556663..29b574738d 100644 --- a/libretro-common/file/nbio/nbio_unixmmap.c +++ b/libretro-common/file/nbio/nbio_unixmmap.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (nbio_unixmmap.c). diff --git a/libretro-common/file/nbio/nbio_windowsmmap.c b/libretro-common/file/nbio/nbio_windowsmmap.c index ff9d9d46e4..2bfd834a71 100644 --- a/libretro-common/file/nbio/nbio_windowsmmap.c +++ b/libretro-common/file/nbio/nbio_windowsmmap.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (nbio_windowsmmap.c). diff --git a/libretro-common/file/retro_dirent.c b/libretro-common/file/retro_dirent.c index dc3fa5dde0..0b6ecad246 100644 --- a/libretro-common/file/retro_dirent.c +++ b/libretro-common/file/retro_dirent.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_dirent.c). diff --git a/libretro-common/formats/bmp/rbmp.c b/libretro-common/formats/bmp/rbmp.c index ee2b06f01d..1e7eaa9349 100644 --- a/libretro-common/formats/bmp/rbmp.c +++ b/libretro-common/formats/bmp/rbmp.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rbmp.c). diff --git a/libretro-common/formats/bmp/rbmp_encode.c b/libretro-common/formats/bmp/rbmp_encode.c index 8b22f5ce31..50798b080f 100644 --- a/libretro-common/formats/bmp/rbmp_encode.c +++ b/libretro-common/formats/bmp/rbmp_encode.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rbmp_encode.c). diff --git a/libretro-common/formats/image_texture.c b/libretro-common/formats/image_texture.c index c531335e7b..e2f3c3d162 100644 --- a/libretro-common/formats/image_texture.c +++ b/libretro-common/formats/image_texture.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (image_texture.c). diff --git a/libretro-common/formats/image_transfer.c b/libretro-common/formats/image_transfer.c index 4c2bb5dd15..3f78b5356a 100644 --- a/libretro-common/formats/image_transfer.c +++ b/libretro-common/formats/image_transfer.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (image_transfer.c). diff --git a/libretro-common/formats/jpeg/rjpeg.c b/libretro-common/formats/jpeg/rjpeg.c index 49490e82eb..0b1f7ea2c6 100644 --- a/libretro-common/formats/jpeg/rjpeg.c +++ b/libretro-common/formats/jpeg/rjpeg.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rjpeg.c). diff --git a/libretro-common/formats/json/jsonsax.c b/libretro-common/formats/json/jsonsax.c index fec8a7df8a..ec57c004da 100644 --- a/libretro-common/formats/json/jsonsax.c +++ b/libretro-common/formats/json/jsonsax.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (jsonsax.c). diff --git a/libretro-common/formats/png/rpng.c b/libretro-common/formats/png/rpng.c index b4b9c6c675..e28272124c 100644 --- a/libretro-common/formats/png/rpng.c +++ b/libretro-common/formats/png/rpng.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rpng.c). diff --git a/libretro-common/formats/png/rpng_encode.c b/libretro-common/formats/png/rpng_encode.c index 92e25e16f4..3500c3aaf4 100644 --- a/libretro-common/formats/png/rpng_encode.c +++ b/libretro-common/formats/png/rpng_encode.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rpng_encode.c). diff --git a/libretro-common/formats/png/rpng_internal.h b/libretro-common/formats/png/rpng_internal.h index 440bd3d587..62dcfbc9e0 100644 --- a/libretro-common/formats/png/rpng_internal.h +++ b/libretro-common/formats/png/rpng_internal.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rpng_internal.h). diff --git a/libretro-common/formats/tga/rtga.c b/libretro-common/formats/tga/rtga.c index 7e4e62e306..c96e7ba8f4 100644 --- a/libretro-common/formats/tga/rtga.c +++ b/libretro-common/formats/tga/rtga.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rtga.c). diff --git a/libretro-common/formats/wav/rwav.c b/libretro-common/formats/wav/rwav.c index 7e43ade3ea..e012005ae8 100644 --- a/libretro-common/formats/wav/rwav.c +++ b/libretro-common/formats/wav/rwav.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rwav.c). diff --git a/libretro-common/formats/xml/rxml.c b/libretro-common/formats/xml/rxml.c index b7129653c4..e2a02a77c5 100644 --- a/libretro-common/formats/xml/rxml.c +++ b/libretro-common/formats/xml/rxml.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rxml.c). diff --git a/libretro-common/formats/xml/test/rxml_test.c b/libretro-common/formats/xml/test/rxml_test.c index e966b1d60e..36e794eefc 100644 --- a/libretro-common/formats/xml/test/rxml_test.c +++ b/libretro-common/formats/xml/test/rxml_test.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rxml_test.c). diff --git a/libretro-common/gfx/gl_capabilities.c b/libretro-common/gfx/gl_capabilities.c index d9c8d147b2..d7ec069e4b 100644 --- a/libretro-common/gfx/gl_capabilities.c +++ b/libretro-common/gfx/gl_capabilities.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (gl_capabilities.c). diff --git a/libretro-common/gfx/scaler/pixconv.c b/libretro-common/gfx/scaler/pixconv.c index 604479e404..64f5d89d0d 100644 --- a/libretro-common/gfx/scaler/pixconv.c +++ b/libretro-common/gfx/scaler/pixconv.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (pixconv.c). diff --git a/libretro-common/gfx/scaler/scaler.c b/libretro-common/gfx/scaler/scaler.c index 24b2d0c919..803fecb1aa 100644 --- a/libretro-common/gfx/scaler/scaler.c +++ b/libretro-common/gfx/scaler/scaler.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (scaler.c). diff --git a/libretro-common/gfx/scaler/scaler_filter.c b/libretro-common/gfx/scaler/scaler_filter.c index 6134e99f71..498f80d8f3 100644 --- a/libretro-common/gfx/scaler/scaler_filter.c +++ b/libretro-common/gfx/scaler/scaler_filter.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (scaler_filter.c). diff --git a/libretro-common/gfx/scaler/scaler_int.c b/libretro-common/gfx/scaler/scaler_int.c index 42c8ab58bf..9aa583cef8 100644 --- a/libretro-common/gfx/scaler/scaler_int.c +++ b/libretro-common/gfx/scaler/scaler_int.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (scaler_int.c). diff --git a/libretro-common/glsm/glsm.c b/libretro-common/glsm/glsm.c index 0f01a2706f..85a7ced78a 100644 --- a/libretro-common/glsm/glsm.c +++ b/libretro-common/glsm/glsm.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro SDK code part (glsm). diff --git a/libretro-common/glsym/glsym_gl.c b/libretro-common/glsym/glsym_gl.c index 5aa92fdca2..d89992c919 100644 --- a/libretro-common/glsym/glsym_gl.c +++ b/libretro-common/glsym/glsym_gl.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro SDK code part (glsym). diff --git a/libretro-common/glsym/rglgen.c b/libretro-common/glsym/rglgen.c index 0ea1fdc99c..6306eaef47 100644 --- a/libretro-common/glsym/rglgen.c +++ b/libretro-common/glsym/rglgen.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro SDK code part (glsym). diff --git a/libretro-common/hash/rhash.c b/libretro-common/hash/rhash.c index 616e85990c..b123d4d09b 100644 --- a/libretro-common/hash/rhash.c +++ b/libretro-common/hash/rhash.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rhash.c). diff --git a/libretro-common/include/audio/audio_mix.h b/libretro-common/include/audio/audio_mix.h index 3b59dff689..937c4b28e8 100644 --- a/libretro-common/include/audio/audio_mix.h +++ b/libretro-common/include/audio/audio_mix.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (audio_mix.h). diff --git a/libretro-common/include/audio/audio_mixer.h b/libretro-common/include/audio/audio_mixer.h index 3df9c64931..ec319bc620 100644 --- a/libretro-common/include/audio/audio_mixer.h +++ b/libretro-common/include/audio/audio_mixer.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (audio_mixer.h). diff --git a/libretro-common/include/audio/audio_resampler.h b/libretro-common/include/audio/audio_resampler.h index d950586aea..46eb426a20 100644 --- a/libretro-common/include/audio/audio_resampler.h +++ b/libretro-common/include/audio/audio_resampler.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (audio_resampler.h). diff --git a/libretro-common/include/audio/conversion/float_to_s16.h b/libretro-common/include/audio/conversion/float_to_s16.h index 30903cfd4c..2ddf65e75d 100644 --- a/libretro-common/include/audio/conversion/float_to_s16.h +++ b/libretro-common/include/audio/conversion/float_to_s16.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (float_to_s16.h). diff --git a/libretro-common/include/audio/conversion/s16_to_float.h b/libretro-common/include/audio/conversion/s16_to_float.h index ebe43ac4f4..4f705b8b80 100644 --- a/libretro-common/include/audio/conversion/s16_to_float.h +++ b/libretro-common/include/audio/conversion/s16_to_float.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (s16_to_float.h). diff --git a/libretro-common/include/audio/dsp_filter.h b/libretro-common/include/audio/dsp_filter.h index 2055235f6f..82af77135c 100644 --- a/libretro-common/include/audio/dsp_filter.h +++ b/libretro-common/include/audio/dsp_filter.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (dsp_filter.h). diff --git a/libretro-common/include/boolean.h b/libretro-common/include/boolean.h index 8a5482cde5..f06ac5a742 100644 --- a/libretro-common/include/boolean.h +++ b/libretro-common/include/boolean.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (boolean.h). diff --git a/libretro-common/include/clamping.h b/libretro-common/include/clamping.h index 7919332b95..ec29bf9a38 100644 --- a/libretro-common/include/clamping.h +++ b/libretro-common/include/clamping.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (clamping.h). diff --git a/libretro-common/include/compat/apple_compat.h b/libretro-common/include/compat/apple_compat.h index f656546302..819b39ecf6 100644 --- a/libretro-common/include/compat/apple_compat.h +++ b/libretro-common/include/compat/apple_compat.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (apple_compat.h). diff --git a/libretro-common/include/compat/fnmatch.h b/libretro-common/include/compat/fnmatch.h index 3f3c0254bc..cede1ca6c7 100644 --- a/libretro-common/include/compat/fnmatch.h +++ b/libretro-common/include/compat/fnmatch.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (fnmatch.h). diff --git a/libretro-common/include/compat/fopen_utf8.h b/libretro-common/include/compat/fopen_utf8.h index 1fe6a8aab6..f59822a5ca 100644 --- a/libretro-common/include/compat/fopen_utf8.h +++ b/libretro-common/include/compat/fopen_utf8.h @@ -1,8 +1,30 @@ -#ifndef __FOPEN_UTF8_H -#define __FOPEN_UTF8_H +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (fopen_utf8.h). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + +#ifndef __LIBRETRO_SDK_COMPAT_FOPEN_UTF8_H +#define __LIBRETRO_SDK_COMPAT_FOPEN_UTF8_H #ifdef _WIN32 -/* defined to error rather than fopen_utf8, to make it clear to everyone reading the code that not worrying about utf16 is fine */ +/* Defined to error rather than fopen_utf8, to make it clear to everyone reading the code that not worrying about utf16 is fine */ /* TODO: enable */ /* #define fopen (use fopen_utf8 instead) */ void *fopen_utf8(const char * filename, const char * mode); diff --git a/libretro-common/include/compat/getopt.h b/libretro-common/include/compat/getopt.h index f685681559..74287b64f5 100644 --- a/libretro-common/include/compat/getopt.h +++ b/libretro-common/include/compat/getopt.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (getopt.h). diff --git a/libretro-common/include/compat/intrinsics.h b/libretro-common/include/compat/intrinsics.h index 5d6e8a5b60..3fc7d92918 100644 --- a/libretro-common/include/compat/intrinsics.h +++ b/libretro-common/include/compat/intrinsics.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (intrinsics.h). diff --git a/libretro-common/include/compat/msvc.h b/libretro-common/include/compat/msvc.h index fea4b53660..822c97339a 100644 --- a/libretro-common/include/compat/msvc.h +++ b/libretro-common/include/compat/msvc.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (msvc.h). diff --git a/libretro-common/include/compat/posix_string.h b/libretro-common/include/compat/posix_string.h index 380e1a121f..9f56322ab9 100644 --- a/libretro-common/include/compat/posix_string.h +++ b/libretro-common/include/compat/posix_string.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (posix_string.h). diff --git a/libretro-common/include/compat/strcasestr.h b/libretro-common/include/compat/strcasestr.h index 376f1003fd..f849593b0f 100644 --- a/libretro-common/include/compat/strcasestr.h +++ b/libretro-common/include/compat/strcasestr.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (strcasestr.h). diff --git a/libretro-common/include/compat/strl.h b/libretro-common/include/compat/strl.h index 853aceffcc..290498d9a6 100644 --- a/libretro-common/include/compat/strl.h +++ b/libretro-common/include/compat/strl.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (strl.h). diff --git a/libretro-common/include/dynamic/dylib.h b/libretro-common/include/dynamic/dylib.h index b8ee171cf3..f6951273bd 100644 --- a/libretro-common/include/dynamic/dylib.h +++ b/libretro-common/include/dynamic/dylib.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (dylib.h). diff --git a/libretro-common/include/encodings/crc32.h b/libretro-common/include/encodings/crc32.h index 0c9947629d..76fbb5475e 100644 --- a/libretro-common/include/encodings/crc32.h +++ b/libretro-common/include/encodings/crc32.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (crc32.h). diff --git a/libretro-common/include/encodings/utf.h b/libretro-common/include/encodings/utf.h index bad9458966..b513f28a18 100644 --- a/libretro-common/include/encodings/utf.h +++ b/libretro-common/include/encodings/utf.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (utf.h). diff --git a/libretro-common/include/encodings/win32.h b/libretro-common/include/encodings/win32.h index 7d8057e8b8..d4221bd9b7 100644 --- a/libretro-common/include/encodings/win32.h +++ b/libretro-common/include/encodings/win32.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2016 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (utf.h). diff --git a/libretro-common/include/fastcpy.h b/libretro-common/include/fastcpy.h index 13b94d97bd..9d40702bf3 100644 --- a/libretro-common/include/fastcpy.h +++ b/libretro-common/include/fastcpy.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (fastcpy.h). diff --git a/libretro-common/include/features/features_cpu.h b/libretro-common/include/features/features_cpu.h index fd8f5f62b6..0aa043dad0 100644 --- a/libretro-common/include/features/features_cpu.h +++ b/libretro-common/include/features/features_cpu.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (features_cpu.h). diff --git a/libretro-common/include/file/archive_file.h b/libretro-common/include/file/archive_file.h index 7f5ab9fb92..6cc7d4725b 100644 --- a/libretro-common/include/file/archive_file.h +++ b/libretro-common/include/file/archive_file.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (archive_file.h). diff --git a/libretro-common/include/file/config_file.h b/libretro-common/include/file/config_file.h index 3c0d99987a..6b4dde77e7 100644 --- a/libretro-common/include/file/config_file.h +++ b/libretro-common/include/file/config_file.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (config_file.h). diff --git a/libretro-common/include/file/config_file_userdata.h b/libretro-common/include/file/config_file_userdata.h index 0c433ed361..ca6f691785 100644 --- a/libretro-common/include/file/config_file_userdata.h +++ b/libretro-common/include/file/config_file_userdata.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (config_file_userdata.h). diff --git a/libretro-common/include/file/file_path.h b/libretro-common/include/file/file_path.h index 4a29494d98..a7ee1e7c54 100644 --- a/libretro-common/include/file/file_path.h +++ b/libretro-common/include/file/file_path.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (file_path.h). diff --git a/libretro-common/include/file/nbio.h b/libretro-common/include/file/nbio.h index 554bd1103d..4b5e5a41bc 100644 --- a/libretro-common/include/file/nbio.h +++ b/libretro-common/include/file/nbio.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (nbio.h). diff --git a/libretro-common/include/filters.h b/libretro-common/include/filters.h index 53743bba7f..69048efd90 100644 --- a/libretro-common/include/filters.h +++ b/libretro-common/include/filters.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (filters.h). diff --git a/libretro-common/include/formats/image.h b/libretro-common/include/formats/image.h index bba886642d..20a3c8058a 100644 --- a/libretro-common/include/formats/image.h +++ b/libretro-common/include/formats/image.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (image.h). diff --git a/libretro-common/include/formats/jsonsax.h b/libretro-common/include/formats/jsonsax.h index d8b18e6b8d..c98331fb39 100644 --- a/libretro-common/include/formats/jsonsax.h +++ b/libretro-common/include/formats/jsonsax.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (jsonsax.h). diff --git a/libretro-common/include/formats/rbmp.h b/libretro-common/include/formats/rbmp.h index 622d8c94d7..9b3a908733 100644 --- a/libretro-common/include/formats/rbmp.h +++ b/libretro-common/include/formats/rbmp.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rbmp.h). diff --git a/libretro-common/include/formats/rjpeg.h b/libretro-common/include/formats/rjpeg.h index a999516df0..ed4ee3eea1 100644 --- a/libretro-common/include/formats/rjpeg.h +++ b/libretro-common/include/formats/rjpeg.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rjpeg.h). diff --git a/libretro-common/include/formats/rpng.h b/libretro-common/include/formats/rpng.h index b16a8307fa..aeae7af49a 100644 --- a/libretro-common/include/formats/rpng.h +++ b/libretro-common/include/formats/rpng.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rpng.h). diff --git a/libretro-common/include/formats/rtga.h b/libretro-common/include/formats/rtga.h index a66f4a0ab6..1b9409c365 100644 --- a/libretro-common/include/formats/rtga.h +++ b/libretro-common/include/formats/rtga.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rtga.h). diff --git a/libretro-common/include/formats/rwav.h b/libretro-common/include/formats/rwav.h index 3f4ed170e3..198d24b5b5 100644 --- a/libretro-common/include/formats/rwav.h +++ b/libretro-common/include/formats/rwav.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rwav.h). diff --git a/libretro-common/include/formats/rxml.h b/libretro-common/include/formats/rxml.h index 13bcebac1f..78d01a2972 100644 --- a/libretro-common/include/formats/rxml.h +++ b/libretro-common/include/formats/rxml.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rxml.h). diff --git a/libretro-common/include/gfx/gl_capabilities.h b/libretro-common/include/gfx/gl_capabilities.h index 4e7c97a0a7..c71352efaa 100644 --- a/libretro-common/include/gfx/gl_capabilities.h +++ b/libretro-common/include/gfx/gl_capabilities.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (gl_capabilities.h). diff --git a/libretro-common/include/gfx/math/matrix_3x3.h b/libretro-common/include/gfx/math/matrix_3x3.h index 9bdd886945..791f1dae06 100644 --- a/libretro-common/include/gfx/math/matrix_3x3.h +++ b/libretro-common/include/gfx/math/matrix_3x3.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (matrix_3x3.h). diff --git a/libretro-common/include/gfx/math/matrix_4x4.h b/libretro-common/include/gfx/math/matrix_4x4.h index de5244ef26..dacb37c31c 100644 --- a/libretro-common/include/gfx/math/matrix_4x4.h +++ b/libretro-common/include/gfx/math/matrix_4x4.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (matrix_4x4.h). diff --git a/libretro-common/include/gfx/math/vector_2.h b/libretro-common/include/gfx/math/vector_2.h index 1265c6de45..a38153ea2c 100644 --- a/libretro-common/include/gfx/math/vector_2.h +++ b/libretro-common/include/gfx/math/vector_2.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (vector_2.h). diff --git a/libretro-common/include/gfx/math/vector_3.h b/libretro-common/include/gfx/math/vector_3.h index 67256172f0..40c9f7f49c 100644 --- a/libretro-common/include/gfx/math/vector_3.h +++ b/libretro-common/include/gfx/math/vector_3.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (vector_3.h). diff --git a/libretro-common/include/gfx/math/vector_4.h b/libretro-common/include/gfx/math/vector_4.h index 3b9510c511..e985649206 100644 --- a/libretro-common/include/gfx/math/vector_4.h +++ b/libretro-common/include/gfx/math/vector_4.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (vector_4.h). diff --git a/libretro-common/include/gfx/scaler/filter.h b/libretro-common/include/gfx/scaler/filter.h index ba5ae93907..27f677a6c3 100644 --- a/libretro-common/include/gfx/scaler/filter.h +++ b/libretro-common/include/gfx/scaler/filter.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (filter.h). diff --git a/libretro-common/include/gfx/scaler/pixconv.h b/libretro-common/include/gfx/scaler/pixconv.h index 6dfab47a9a..100864de68 100644 --- a/libretro-common/include/gfx/scaler/pixconv.h +++ b/libretro-common/include/gfx/scaler/pixconv.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (pixconv.h). diff --git a/libretro-common/include/gfx/scaler/scaler.h b/libretro-common/include/gfx/scaler/scaler.h index 8d97515616..97854d45b3 100644 --- a/libretro-common/include/gfx/scaler/scaler.h +++ b/libretro-common/include/gfx/scaler/scaler.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (scaler.h). diff --git a/libretro-common/include/gfx/scaler/scaler_int.h b/libretro-common/include/gfx/scaler/scaler_int.h index 87ef68eda2..2cb203a3ba 100644 --- a/libretro-common/include/gfx/scaler/scaler_int.h +++ b/libretro-common/include/gfx/scaler/scaler_int.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (scaler_int.h). diff --git a/libretro-common/include/gfx/video_frame.h b/libretro-common/include/gfx/video_frame.h index e5c0540d9c..c00dc81d57 100644 --- a/libretro-common/include/gfx/video_frame.h +++ b/libretro-common/include/gfx/video_frame.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (video_frame.h). diff --git a/libretro-common/include/glsm/glsm.h b/libretro-common/include/glsm/glsm.h index 8e30acdc04..d422267d11 100644 --- a/libretro-common/include/glsm/glsm.h +++ b/libretro-common/include/glsm/glsm.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro SDK code part (glsm.h). diff --git a/libretro-common/include/glsm/glsmsym.h b/libretro-common/include/glsm/glsmsym.h index 215f3ecb51..3611441919 100644 --- a/libretro-common/include/glsm/glsmsym.h +++ b/libretro-common/include/glsm/glsmsym.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro SDK code part (glsmsym.h). diff --git a/libretro-common/include/glsym/glsym.h b/libretro-common/include/glsym/glsym.h index c1c13e2a56..24f4584bb6 100644 --- a/libretro-common/include/glsym/glsym.h +++ b/libretro-common/include/glsym/glsym.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro SDK code part (glsym). diff --git a/libretro-common/include/glsym/glsym_gl.h b/libretro-common/include/glsym/glsym_gl.h index 9720c2e1cf..1934287f2a 100644 --- a/libretro-common/include/glsym/glsym_gl.h +++ b/libretro-common/include/glsym/glsym_gl.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro SDK code part (glsym). diff --git a/libretro-common/include/glsym/rglgen.h b/libretro-common/include/glsym/rglgen.h index 54dcb62a84..b8a1a0c877 100644 --- a/libretro-common/include/glsym/rglgen.h +++ b/libretro-common/include/glsym/rglgen.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro SDK code part (glsym). diff --git a/libretro-common/include/glsym/rglgen_headers.h b/libretro-common/include/glsym/rglgen_headers.h index 06d8530b27..766efb6361 100644 --- a/libretro-common/include/glsym/rglgen_headers.h +++ b/libretro-common/include/glsym/rglgen_headers.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro SDK code part (glsym). diff --git a/libretro-common/include/libco.h b/libretro-common/include/libco.h index 1599cb2ca0..851b29cc43 100644 --- a/libretro-common/include/libco.h +++ b/libretro-common/include/libco.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (libco.h). diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 7d0118526f..804a6c26e6 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro API header (libretro.h). diff --git a/libretro-common/include/libretro_d3d.h b/libretro-common/include/libretro_d3d.h index 7c44d8d52d..d95931b3e9 100644 --- a/libretro-common/include/libretro_d3d.h +++ b/libretro-common/include/libretro_d3d.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2016 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------------- * The following license statement only applies to this libretro API header (libretro_d3d.h) diff --git a/libretro-common/include/libretro_dspfilter.h b/libretro-common/include/libretro_dspfilter.h index 4de515d01b..36bfd88d5f 100644 --- a/libretro-common/include/libretro_dspfilter.h +++ b/libretro-common/include/libretro_dspfilter.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro API header (libretro_dspfilter.h). diff --git a/libretro-common/include/libretro_vulkan.h b/libretro-common/include/libretro_vulkan.h index e1005a163b..5ae7187b7d 100644 --- a/libretro-common/include/libretro_vulkan.h +++ b/libretro-common/include/libretro_vulkan.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------------- * The following license statement only applies to this libretro API header (libretro_vulkan.h) diff --git a/libretro-common/include/lists/dir_list.h b/libretro-common/include/lists/dir_list.h index 3b4a9c573d..4babb02d39 100644 --- a/libretro-common/include/lists/dir_list.h +++ b/libretro-common/include/lists/dir_list.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (dir_list.h). diff --git a/libretro-common/include/lists/file_list.h b/libretro-common/include/lists/file_list.h index f6715a3572..208bf15a59 100644 --- a/libretro-common/include/lists/file_list.h +++ b/libretro-common/include/lists/file_list.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (file_list.h). diff --git a/libretro-common/include/lists/string_list.h b/libretro-common/include/lists/string_list.h index c15cd0eca7..513876e6d1 100644 --- a/libretro-common/include/lists/string_list.h +++ b/libretro-common/include/lists/string_list.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (string_list.h). diff --git a/libretro-common/include/math/complex.h b/libretro-common/include/math/complex.h index d7695c2d22..abfbfa60f5 100644 --- a/libretro-common/include/math/complex.h +++ b/libretro-common/include/math/complex.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (complex.h). diff --git a/libretro-common/include/math/float_minmax.h b/libretro-common/include/math/float_minmax.h index d917ef8ef6..2164dde0f7 100644 --- a/libretro-common/include/math/float_minmax.h +++ b/libretro-common/include/math/float_minmax.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (float_minmax.h). diff --git a/libretro-common/include/math/fxp.h b/libretro-common/include/math/fxp.h index 56b036cd72..46f58bae91 100644 --- a/libretro-common/include/math/fxp.h +++ b/libretro-common/include/math/fxp.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (fxp.h). diff --git a/libretro-common/include/memalign.h b/libretro-common/include/memalign.h index ca809f80c3..2c07a7c053 100644 --- a/libretro-common/include/memalign.h +++ b/libretro-common/include/memalign.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (memalign.h). diff --git a/libretro-common/include/memmap.h b/libretro-common/include/memmap.h index 8d939c4351..2bedd5c926 100644 --- a/libretro-common/include/memmap.h +++ b/libretro-common/include/memmap.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (memmap.h). diff --git a/libretro-common/include/net/net_compat.h b/libretro-common/include/net/net_compat.h index 7a182af2af..789f944e71 100644 --- a/libretro-common/include/net/net_compat.h +++ b/libretro-common/include/net/net_compat.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_compat.h). diff --git a/libretro-common/include/net/net_http.h b/libretro-common/include/net/net_http.h index 8a9f2c5e0b..f98462764d 100644 --- a/libretro-common/include/net/net_http.h +++ b/libretro-common/include/net/net_http.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_http.h). diff --git a/libretro-common/include/net/net_http_parse.h b/libretro-common/include/net/net_http_parse.h index 4fc4845cd0..6e957d3975 100644 --- a/libretro-common/include/net/net_http_parse.h +++ b/libretro-common/include/net/net_http_parse.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_http.h). diff --git a/libretro-common/include/net/net_ifinfo.h b/libretro-common/include/net/net_ifinfo.h index 9949da2f05..b1d2535500 100644 --- a/libretro-common/include/net/net_ifinfo.h +++ b/libretro-common/include/net/net_ifinfo.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_ifinfo.h). diff --git a/libretro-common/include/net/net_natt.h b/libretro-common/include/net/net_natt.h index 9c8b3d7d7d..a755480808 100644 --- a/libretro-common/include/net/net_natt.h +++ b/libretro-common/include/net/net_natt.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_natt.h). diff --git a/libretro-common/include/net/net_socket.h b/libretro-common/include/net/net_socket.h index 5d6dd8784e..098854e658 100644 --- a/libretro-common/include/net/net_socket.h +++ b/libretro-common/include/net/net_socket.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_socket.h). diff --git a/libretro-common/include/net/net_socket_ssl.h b/libretro-common/include/net/net_socket_ssl.h index c9ca75dad0..2691a2d12d 100644 --- a/libretro-common/include/net/net_socket_ssl.h +++ b/libretro-common/include/net/net_socket_ssl.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_socket.h). diff --git a/libretro-common/include/queues/fifo_queue.h b/libretro-common/include/queues/fifo_queue.h index f1a3cae13c..b9c22e7ece 100644 --- a/libretro-common/include/queues/fifo_queue.h +++ b/libretro-common/include/queues/fifo_queue.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (fifo_queue.h). diff --git a/libretro-common/include/queues/message_queue.h b/libretro-common/include/queues/message_queue.h index 2339e93578..760ba279d8 100644 --- a/libretro-common/include/queues/message_queue.h +++ b/libretro-common/include/queues/message_queue.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (message_queue.h). diff --git a/libretro-common/include/queues/task_queue.h b/libretro-common/include/queues/task_queue.h index 8e25cc43ac..40c35ddfdb 100644 --- a/libretro-common/include/queues/task_queue.h +++ b/libretro-common/include/queues/task_queue.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (task_queue.h). diff --git a/libretro-common/include/retro_assert.h b/libretro-common/include/retro_assert.h index 3ef0300ec6..9f3abdeafb 100644 --- a/libretro-common/include/retro_assert.h +++ b/libretro-common/include/retro_assert.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_assert.h). diff --git a/libretro-common/include/retro_common.h b/libretro-common/include/retro_common.h index 5938bea1b5..e4804fae08 100644 --- a/libretro-common/include/retro_common.h +++ b/libretro-common/include/retro_common.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_common.h). diff --git a/libretro-common/include/retro_common_api.h b/libretro-common/include/retro_common_api.h index ff38f9837c..c3b3f4927f 100644 --- a/libretro-common/include/retro_common_api.h +++ b/libretro-common/include/retro_common_api.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_common_api.h). diff --git a/libretro-common/include/retro_dirent.h b/libretro-common/include/retro_dirent.h index 51b7eac700..c3450732c5 100644 --- a/libretro-common/include/retro_dirent.h +++ b/libretro-common/include/retro_dirent.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_dirent.h). diff --git a/libretro-common/include/retro_endianness.h b/libretro-common/include/retro_endianness.h index fc9299e178..e721ec9d4d 100644 --- a/libretro-common/include/retro_endianness.h +++ b/libretro-common/include/retro_endianness.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_endianness.h). diff --git a/libretro-common/include/retro_environment.h b/libretro-common/include/retro_environment.h index 12ee429de8..1a18cd6f8c 100644 --- a/libretro-common/include/retro_environment.h +++ b/libretro-common/include/retro_environment.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_environment.h). diff --git a/libretro-common/include/retro_inline.h b/libretro-common/include/retro_inline.h index ffdaa4a3ab..e4a21f6c41 100644 --- a/libretro-common/include/retro_inline.h +++ b/libretro-common/include/retro_inline.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_inline.h). diff --git a/libretro-common/include/retro_math.h b/libretro-common/include/retro_math.h index c0efb23bd9..03d31dd546 100644 --- a/libretro-common/include/retro_math.h +++ b/libretro-common/include/retro_math.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_math.h). diff --git a/libretro-common/include/retro_miscellaneous.h b/libretro-common/include/retro_miscellaneous.h index 23f9ef02ac..afcb885cf7 100644 --- a/libretro-common/include/retro_miscellaneous.h +++ b/libretro-common/include/retro_miscellaneous.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_miscellaneous.h). diff --git a/libretro-common/include/retro_timers.h b/libretro-common/include/retro_timers.h index 0cd4b4c48b..e56e18fc38 100644 --- a/libretro-common/include/retro_timers.h +++ b/libretro-common/include/retro_timers.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_timers.h). diff --git a/libretro-common/include/rhash.h b/libretro-common/include/rhash.h index d7394885bd..6ed9149f4d 100644 --- a/libretro-common/include/rhash.h +++ b/libretro-common/include/rhash.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rhash.h). diff --git a/libretro-common/include/rthreads/rthreads.h b/libretro-common/include/rthreads/rthreads.h index c9448ec702..6e248f3d94 100644 --- a/libretro-common/include/rthreads/rthreads.h +++ b/libretro-common/include/rthreads/rthreads.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rthreads.h). diff --git a/libretro-common/include/streams/chd_stream.h b/libretro-common/include/streams/chd_stream.h index 53c364fb26..246a2dcfc9 100644 --- a/libretro-common/include/streams/chd_stream.h +++ b/libretro-common/include/streams/chd_stream.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (chd_stream.h). diff --git a/libretro-common/include/streams/file_stream.h b/libretro-common/include/streams/file_stream.h index 1ae545dc8c..546fd12697 100644 --- a/libretro-common/include/streams/file_stream.h +++ b/libretro-common/include/streams/file_stream.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (file_stream.h). diff --git a/libretro-common/include/streams/file_stream_transforms.h b/libretro-common/include/streams/file_stream_transforms.h index 0cd565b61c..7919f6fbbd 100644 --- a/libretro-common/include/streams/file_stream_transforms.h +++ b/libretro-common/include/streams/file_stream_transforms.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (file_stream_transforms.h). diff --git a/libretro-common/include/streams/interface_stream.h b/libretro-common/include/streams/interface_stream.h index 8ab80737f1..71d5c97fd4 100644 --- a/libretro-common/include/streams/interface_stream.h +++ b/libretro-common/include/streams/interface_stream.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (interface_stream.h). diff --git a/libretro-common/include/streams/memory_stream.h b/libretro-common/include/streams/memory_stream.h index fba8783ec5..c594808772 100644 --- a/libretro-common/include/streams/memory_stream.h +++ b/libretro-common/include/streams/memory_stream.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (memory_stream.h). diff --git a/libretro-common/include/streams/stdin_stream.h b/libretro-common/include/streams/stdin_stream.h index ce5ebe55df..dda02963e4 100644 --- a/libretro-common/include/streams/stdin_stream.h +++ b/libretro-common/include/streams/stdin_stream.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (stdin_stream.h). diff --git a/libretro-common/include/streams/trans_stream.h b/libretro-common/include/streams/trans_stream.h index 4023103e8a..0a10368e73 100644 --- a/libretro-common/include/streams/trans_stream.h +++ b/libretro-common/include/streams/trans_stream.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (trans_stream.h). diff --git a/libretro-common/include/string/stdstring.h b/libretro-common/include/string/stdstring.h index 17e7209529..eb2954b825 100644 --- a/libretro-common/include/string/stdstring.h +++ b/libretro-common/include/string/stdstring.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (stdstring.h). diff --git a/libretro-common/include/vfs/vfs_implementation.h b/libretro-common/include/vfs/vfs_implementation.h index 5ceae39b6f..047b170354 100644 --- a/libretro-common/include/vfs/vfs_implementation.h +++ b/libretro-common/include/vfs/vfs_implementation.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (vfs_implementation.h). diff --git a/libretro-common/lists/dir_list.c b/libretro-common/lists/dir_list.c index bcd884a177..fc360f748b 100644 --- a/libretro-common/lists/dir_list.c +++ b/libretro-common/lists/dir_list.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (dir_list.c). diff --git a/libretro-common/lists/file_list.c b/libretro-common/lists/file_list.c index c52523e193..1525d8879e 100644 --- a/libretro-common/lists/file_list.c +++ b/libretro-common/lists/file_list.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (file_list.c). diff --git a/libretro-common/lists/string_list.c b/libretro-common/lists/string_list.c index a5cb4be298..3a5fb96fef 100644 --- a/libretro-common/lists/string_list.c +++ b/libretro-common/lists/string_list.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (string_list.c). diff --git a/libretro-common/lists/vector_list.c b/libretro-common/lists/vector_list.c index 4b8c2314e6..b8493c5675 100644 --- a/libretro-common/lists/vector_list.c +++ b/libretro-common/lists/vector_list.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (vector_list.c). diff --git a/libretro-common/memmap/memalign.c b/libretro-common/memmap/memalign.c index 56d0c94669..449d8471b8 100644 --- a/libretro-common/memmap/memalign.c +++ b/libretro-common/memmap/memalign.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (memalign.c). diff --git a/libretro-common/memmap/memmap.c b/libretro-common/memmap/memmap.c index cda86ff01e..26d9ce031f 100644 --- a/libretro-common/memmap/memmap.c +++ b/libretro-common/memmap/memmap.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (memmap.c). diff --git a/libretro-common/net/net_compat.c b/libretro-common/net/net_compat.c index a2950a8586..86c1381221 100644 --- a/libretro-common/net/net_compat.c +++ b/libretro-common/net/net_compat.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_compat.c). diff --git a/libretro-common/net/net_http.c b/libretro-common/net/net_http.c index caa6e56c93..9e916f7942 100644 --- a/libretro-common/net/net_http.c +++ b/libretro-common/net/net_http.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_http.c). diff --git a/libretro-common/net/net_http_parse.c b/libretro-common/net/net_http_parse.c index 69b80f6e37..113eeaa00a 100644 --- a/libretro-common/net/net_http_parse.c +++ b/libretro-common/net/net_http_parse.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_http_parse.c). diff --git a/libretro-common/net/net_ifinfo.c b/libretro-common/net/net_ifinfo.c index e226dda325..77c5971171 100644 --- a/libretro-common/net/net_ifinfo.c +++ b/libretro-common/net/net_ifinfo.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_ifinfo.c). diff --git a/libretro-common/net/net_natt.c b/libretro-common/net/net_natt.c index 34c6f1d648..1924f4d63c 100644 --- a/libretro-common/net/net_natt.c +++ b/libretro-common/net/net_natt.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 The RetroArch team +/* Copyright (C) 2016-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_natt.c). diff --git a/libretro-common/net/net_socket.c b/libretro-common/net/net_socket.c index 0c960360bc..e2213c9bac 100644 --- a/libretro-common/net/net_socket.c +++ b/libretro-common/net/net_socket.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_socket.c). diff --git a/libretro-common/net/net_socket_ssl.c b/libretro-common/net/net_socket_ssl.c index 4b9da1206b..04e0babad1 100644 --- a/libretro-common/net/net_socket_ssl.c +++ b/libretro-common/net/net_socket_ssl.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (net_socket.c). diff --git a/libretro-common/queues/fifo_queue.c b/libretro-common/queues/fifo_queue.c index 85d0268011..d6b21aca48 100644 --- a/libretro-common/queues/fifo_queue.c +++ b/libretro-common/queues/fifo_queue.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (fifo_queue.c). diff --git a/libretro-common/queues/message_queue.c b/libretro-common/queues/message_queue.c index dda5cbf5a1..a8086c3b9f 100644 --- a/libretro-common/queues/message_queue.c +++ b/libretro-common/queues/message_queue.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (message_queue.c). diff --git a/libretro-common/queues/task_queue.c b/libretro-common/queues/task_queue.c index 2d45f65ea0..2eb51dd9e7 100644 --- a/libretro-common/queues/task_queue.c +++ b/libretro-common/queues/task_queue.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (task_queue.c). diff --git a/libretro-common/rthreads/ctr_pthread.h b/libretro-common/rthreads/ctr_pthread.h index 8c82e6e511..0a393ebb8f 100644 --- a/libretro-common/rthreads/ctr_pthread.h +++ b/libretro-common/rthreads/ctr_pthread.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (gx_pthread.h). diff --git a/libretro-common/rthreads/gx_pthread.h b/libretro-common/rthreads/gx_pthread.h index 8cb4616231..055c6afbde 100644 --- a/libretro-common/rthreads/gx_pthread.h +++ b/libretro-common/rthreads/gx_pthread.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (gx_pthread.h). diff --git a/libretro-common/rthreads/rthreads.c b/libretro-common/rthreads/rthreads.c index 9632c2a2d6..e155075555 100644 --- a/libretro-common/rthreads/rthreads.c +++ b/libretro-common/rthreads/rthreads.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (rthreads.c). diff --git a/libretro-common/rthreads/xenon_sdl_threads.c b/libretro-common/rthreads/xenon_sdl_threads.c index 0097673528..ff1855c275 100644 --- a/libretro-common/rthreads/xenon_sdl_threads.c +++ b/libretro-common/rthreads/xenon_sdl_threads.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (xenon_sdl_threads.c). diff --git a/libretro-common/streams/chd_stream.c b/libretro-common/streams/chd_stream.c index 406e9197e6..669bd43391 100644 --- a/libretro-common/streams/chd_stream.c +++ b/libretro-common/streams/chd_stream.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (chd_stream.c). diff --git a/libretro-common/streams/file_stream.c b/libretro-common/streams/file_stream.c index a73b78fbff..5ccdbcfacb 100644 --- a/libretro-common/streams/file_stream.c +++ b/libretro-common/streams/file_stream.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (file_stream.c). diff --git a/libretro-common/streams/file_stream_transforms.c b/libretro-common/streams/file_stream_transforms.c index 143db89c03..c35a337388 100644 --- a/libretro-common/streams/file_stream_transforms.c +++ b/libretro-common/streams/file_stream_transforms.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (file_stream_transforms.c). diff --git a/libretro-common/streams/interface_stream.c b/libretro-common/streams/interface_stream.c index e495d9a9bf..0207823ce2 100644 --- a/libretro-common/streams/interface_stream.c +++ b/libretro-common/streams/interface_stream.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (interface_stream.c). diff --git a/libretro-common/streams/memory_stream.c b/libretro-common/streams/memory_stream.c index 784d0feea1..77a2c008d2 100644 --- a/libretro-common/streams/memory_stream.c +++ b/libretro-common/streams/memory_stream.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (memory_stream.c). diff --git a/libretro-common/streams/stdin_stream.c b/libretro-common/streams/stdin_stream.c index c30c0f4575..6aedfae388 100644 --- a/libretro-common/streams/stdin_stream.c +++ b/libretro-common/streams/stdin_stream.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (stdin_stream.c). diff --git a/libretro-common/streams/trans_stream.c b/libretro-common/streams/trans_stream.c index 10eea457a7..92f3207d64 100644 --- a/libretro-common/streams/trans_stream.c +++ b/libretro-common/streams/trans_stream.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (trans_stream.c). diff --git a/libretro-common/streams/trans_stream_pipe.c b/libretro-common/streams/trans_stream_pipe.c index f30d5df390..5a2e076652 100644 --- a/libretro-common/streams/trans_stream_pipe.c +++ b/libretro-common/streams/trans_stream_pipe.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (trans_stream_pipe.c). diff --git a/libretro-common/streams/trans_stream_zlib.c b/libretro-common/streams/trans_stream_zlib.c index ec350119ca..7f797f12d6 100644 --- a/libretro-common/streams/trans_stream_zlib.c +++ b/libretro-common/streams/trans_stream_zlib.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (trans_stream_zlib.c). diff --git a/libretro-common/string/stdstring.c b/libretro-common/string/stdstring.c index f864cea356..6e1555dbfe 100644 --- a/libretro-common/string/stdstring.c +++ b/libretro-common/string/stdstring.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (stdstring.c). diff --git a/libretro-common/vfs/vfs_implementation.c b/libretro-common/vfs/vfs_implementation.c index ce6fa5bf0e..ff8100f7f1 100644 --- a/libretro-common/vfs/vfs_implementation.c +++ b/libretro-common/vfs/vfs_implementation.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (vfs_implementation.c). From 64b283088962bb22d1e939e9b46848ba8bbac5c5 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Mon, 16 Apr 2018 14:51:38 -0500 Subject: [PATCH 326/517] Fix Win32 build. Set Windows header API to Win7. --- gfx/common/win32_common.c | 113 ++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index d9816ea3ac..b29359a8ce 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -13,6 +13,22 @@ * If not, see . */ +#if !defined(_XBOX) + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 /* Windows 7 */ +#endif + +#if !defined(_MSC_VER) || _WIN32_WINNT >= 0x0601 +#undef WINVER +#define WINVER 0x0601 +#endif + +#define IDI_ICON 1 + +#include +#endif /* !defined(_XBOX) */ + #include #include @@ -33,13 +49,6 @@ #if !defined(_XBOX) -#define IDI_ICON 1 - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0500 /* _WIN32_WINNT_WIN2K */ -#endif - -#include #include #include #include "../../retroarch.h" @@ -284,45 +293,6 @@ void win32_monitor_get_info(void) win32_change_display_settings(current_mon.szDevice, NULL, 0); } -float win32_get_refresh_rate(void *data) -{ - float refresh_rate = 0.0f; -#if _WIN32_WINNT >= 0x0601 || _WIN32_WINDOWS >= 0x0601 /* Win 7 */ - DISPLAYCONFIG_TOPOLOGY_ID TopologyID; - unsigned int NumPathArrayElements = 0; - unsigned int NumModeInfoArrayElements = 0; - DISPLAYCONFIG_PATH_INFO *PathInfoArray = NULL; - DISPLAYCONFIG_MODE_INFO *ModeInfoArray = NULL; - int result = 0; - - GetDisplayConfigBufferSizes(QDC_DATABASE_CURRENT, - &NumPathArrayElements, - &NumModeInfoArrayElements); - - PathInfoArray = (DISPLAYCONFIG_PATH_INFO *) - malloc(sizeof (DISPLAYCONFIG_PATH_INFO) * NumPathArrayElements); - ModeInfoArray = (DISPLAYCONFIG_MODE_INFO *) - malloc(sizeof (DISPLAYCONFIG_MODE_INFO) * NumModeInfoArrayElements); - - result = QueryDisplayConfig(QDC_DATABASE_CURRENT, - &NumPathArrayElements, - PathInfoArray, - &NumModeInfoArrayElements, - ModeInfoArray, - &TopologyID); - if (result == ERROR_SUCCESS && NumPathArrayElements >= 1) - { - refresh_rate = (float) PathInfoArray[0].targetInfo.refreshRate.Numerator / - PathInfoArray[0].targetInfo.refreshRate.Denominator; - } - - free(ModeInfoArray); - free(PathInfoArray); - -#endif - return refresh_rate; -} - void win32_monitor_info(void *data, void *hm_data, unsigned *mon_id) { unsigned i; @@ -1295,6 +1265,57 @@ void win32_get_video_output_prev( } } +float win32_get_refresh_rate(void *data) +{ + float refresh_rate = 0.0f; +#if _WIN32_WINNT >= 0x0601 || _WIN32_WINDOWS >= 0x0601 /* Win 7 */ + OSVERSIONINFO version_info; + DISPLAYCONFIG_TOPOLOGY_ID TopologyID; + unsigned int NumPathArrayElements = 0; + unsigned int NumModeInfoArrayElements = 0; + DISPLAYCONFIG_PATH_INFO *PathInfoArray = NULL; + DISPLAYCONFIG_MODE_INFO *ModeInfoArray = NULL; + int result = 0; + + version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx(&version_info)) + return refresh_rate; + + if (version_info.dwMajorVersion < 6 || + (version_info.dwMajorVersion == 6 && version_info.dwMinorVersion < 1)) + return refresh_rate; + + result = GetDisplayConfigBufferSizes(QDC_DATABASE_CURRENT, + &NumPathArrayElements, + &NumModeInfoArrayElements); + + if (result != ERROR_SUCCESS) + return refresh_rate; + + PathInfoArray = (DISPLAYCONFIG_PATH_INFO *) + malloc(sizeof(DISPLAYCONFIG_PATH_INFO) * NumPathArrayElements); + ModeInfoArray = (DISPLAYCONFIG_MODE_INFO *) + malloc(sizeof(DISPLAYCONFIG_MODE_INFO) * NumModeInfoArrayElements); + + result = QueryDisplayConfig(QDC_DATABASE_CURRENT, + &NumPathArrayElements, + PathInfoArray, + &NumModeInfoArrayElements, + ModeInfoArray, + &TopologyID); + if (result == ERROR_SUCCESS && NumPathArrayElements >= 1) + { + refresh_rate = (float) PathInfoArray[0].targetInfo.refreshRate.Numerator / + PathInfoArray[0].targetInfo.refreshRate.Denominator; + } + + free(ModeInfoArray); + free(PathInfoArray); + +#endif + return refresh_rate; +} + void win32_get_video_output_next( unsigned *width, unsigned *height) { From 3fb2484869db58623ee6487764d97ab2bb3cbe1d Mon Sep 17 00:00:00 2001 From: meleu Date: Mon, 16 Apr 2018 17:01:43 -0300 Subject: [PATCH 327/517] removed unnecessary memory adjustment --- cheevos/var.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cheevos/var.c b/cheevos/var.c index 41884a72b6..e0e6d5c6d8 100644 --- a/cheevos/var.c +++ b/cheevos/var.c @@ -191,12 +191,6 @@ void cheevos_var_patch_addr(cheevos_var_t* var, cheevos_console_t console) var->value -= 0x2000; } } - else if (console == CHEEVOS_CONSOLE_NEOGEO_POCKET) - { - if (var->value >= 0x4000 && var->value <= 0x7fff) - CHEEVOS_LOG(CHEEVOS_TAG "NGP memory address %X adjusted to %X\n", var->value, var->value - 0x004000); - var->value -= 0x4000; - } if (system->mmaps.num_descriptors != 0) { From 95f8205fd1bc3ab75c1b6c8e46582e18fa8b836c Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Mon, 16 Apr 2018 15:36:46 -0500 Subject: [PATCH 328/517] Add DRM interface for refresh rate. --- gfx/common/drm_common.c | 12 ++++++++++++ gfx/common/drm_common.h | 2 ++ gfx/drivers/drm_gfx.c | 6 +++++- gfx/drivers/exynos_gfx.c | 3 ++- gfx/drivers_context/drm_ctx.c | 2 +- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/gfx/common/drm_common.c b/gfx/common/drm_common.c index 33052c83e1..577d2f09a5 100644 --- a/gfx/common/drm_common.c +++ b/gfx/common/drm_common.c @@ -168,6 +168,18 @@ void drm_setup(int fd) RARCH_WARN("[DRM]: Cannot find original CRTC.\n"); } +float drm_get_refresh_rate(void *data) +{ + float refresh_rate = 0.0f; + + if (g_drm_mode) + { + refresh_rate = g_drm_mode->clock * 1000.0f / g_drm_mode->htotal / g_drm_mode->vtotal; + } + + return refresh_rate; +} + void drm_free(void) { if (g_drm_encoder) diff --git a/gfx/common/drm_common.h b/gfx/common/drm_common.h index 87458dac9a..e8cb3ac411 100644 --- a/gfx/common/drm_common.h +++ b/gfx/common/drm_common.h @@ -55,6 +55,8 @@ void drm_free(void); bool drm_get_connector(int fd, video_frame_info_t *video_info); +float drm_get_refresh_rate(void *data); + static INLINE bool drm_wait_flip(int timeout) { g_drm_fds.revents = 0; diff --git a/gfx/drivers/drm_gfx.c b/gfx/drivers/drm_gfx.c index 41416b8b3a..ba069adcb5 100644 --- a/gfx/drivers/drm_gfx.c +++ b/gfx/drivers/drm_gfx.c @@ -39,6 +39,7 @@ #include "../font_driver.h" #include "../../retroarch.h" #include "../../verbosity.h" +#include "../common/drm_common.h" #include "drm_pixformats.h" @@ -683,6 +684,7 @@ static bool init_drm(void) * on exit in case we change it. */ drm.orig_crtc = drmModeGetCrtc(drm.fd, drm.encoder->crtc_id); drm.current_mode = &(drm.orig_crtc->mode); + g_drm_mode = drm.current_mode; /* Set mode physical video mode. Not really needed, but clears TTY console. */ struct modeset_buf buf; @@ -969,7 +971,7 @@ static const video_poke_interface_t drm_poke_interface = { NULL, NULL, NULL, /* set_video_mode */ - NULL, /* get_refresh_rate */ + drm_get_refresh_rate, NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ @@ -1012,6 +1014,8 @@ static void drm_gfx_free(void *data) slock_free(_drmvars->vsync_cond_mutex); scond_free(_drmvars->vsync_condition); + g_drm_mode = NULL; + free(_drmvars); } diff --git a/gfx/drivers/exynos_gfx.c b/gfx/drivers/exynos_gfx.c index 78ed3502c7..659e87bba5 100644 --- a/gfx/drivers/exynos_gfx.c +++ b/gfx/drivers/exynos_gfx.c @@ -663,6 +663,7 @@ static void exynos_deinit(struct exynos_data *pdata) { drm_restore_crtc(); + g_drm_mode = NULL; pdata->width = 0; pdata->height = 0; pdata->num_pages = 0; @@ -1494,7 +1495,7 @@ static const video_poke_interface_t exynos_poke_interface = { NULL, NULL, NULL, /* set_video_mode */ - NULL, /* get_refresh_rate */ + drm_get_refresh_rate, NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c index a4937368f1..434c79502b 100644 --- a/gfx/drivers_context/drm_ctx.c +++ b/gfx/drivers_context/drm_ctx.c @@ -908,7 +908,7 @@ const gfx_ctx_driver_t gfx_ctx_drm = { gfx_ctx_drm_swap_interval, gfx_ctx_drm_set_video_mode, gfx_ctx_drm_get_video_size, - NULL, /* get_refresh_rate */ + drm_get_refresh_rate, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ From 2cf76a192f64bd93ed6d1ebf4f16e9c10f2c8b8a Mon Sep 17 00:00:00 2001 From: Ryunam Date: Mon, 16 Apr 2018 23:21:14 +0200 Subject: [PATCH 329/517] Add Latency list under Settings and move relevant functions --- intl/msg_hash_lbl.h | 4 ++ intl/msg_hash_us.h | 4 ++ menu/cbs/menu_cbs_deferred_push.c | 9 +++++ menu/cbs/menu_cbs_ok.c | 7 ++++ menu/cbs/menu_cbs_sublabel.c | 4 ++ menu/cbs/menu_cbs_title.c | 6 +++ menu/drivers/materialui.c | 2 + menu/menu_cbs.h | 1 + menu/menu_displaylist.c | 66 +++++++++++++++++-------------- menu/menu_displaylist.h | 1 + menu/menu_setting.c | 8 ++++ msg_hash.h | 2 + 12 files changed, 85 insertions(+), 29 deletions(-) diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 3f38faca13..8f138c19d2 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -301,6 +301,8 @@ MSG_HASH(MENU_ENUM_LABEL_DEFERRED_INPUT_HOTKEY_BINDS_LIST, "deferred_input_hotkey_binds") MSG_HASH(MENU_ENUM_LABEL_DEFERRED_INPUT_SETTINGS_LIST, "deferred_input_settings_list") +MSG_HASH(MENU_ENUM_LABEL_DEFERRED_LATENCY_SETTINGS_LIST, + "deferred_latency_settings_list") MSG_HASH(MENU_ENUM_LABEL_DEFERRED_LAKKA_LIST, "deferred_lakka_list") MSG_HASH(MENU_ENUM_LABEL_DEFERRED_LAKKA_SERVICES_LIST, @@ -529,6 +531,8 @@ MSG_HASH(MENU_ENUM_LABEL_INPUT_SETTINGS, "input_settings") MSG_HASH(MENU_ENUM_LABEL_INPUT_SETTINGS_BEGIN, "input_settings_begin") +MSG_HASH(MENU_ENUM_LABEL_LATENCY_SETTINGS, + "latency_settings") MSG_HASH(MENU_ENUM_LABEL_INPUT_SMALL_KEYBOARD_ENABLE, "input_small_keyboard_enable") MSG_HASH(MENU_ENUM_LABEL_INPUT_TOUCH_ENABLE, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index c6de19a42a..923641b474 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -949,6 +949,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_TURBO_PERIOD, "Turbo Period") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_USER_BINDS, "Input User %u Binds") +MSG_HASH(MENU_ENUM_LABEL_VALUE_LATENCY_SETTINGS, + "Latency") MSG_HASH(MENU_ENUM_LABEL_VALUE_INTERNAL_STORAGE_STATUS, "Internal storage status") MSG_HASH(MENU_ENUM_LABEL_VALUE_JOYPAD_AUTOCONFIG_DIR, @@ -1979,6 +1981,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_SETTINGS, "Change joypad, keyboard, and mouse settings.") MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_USER_BINDS, "Configure controls for this user.") +MSG_HASH(MENU_ENUM_SUBLABEL_LATENCY_SETTINGS, + "Change settings related to video, audio and input latency.") MSG_HASH(MENU_ENUM_SUBLABEL_LOG_VERBOSITY, "Enable or disable logging to the terminal.") MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY, diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index 648caf0128..c3bb2362c2 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -155,6 +155,7 @@ generic_deferred_push(deferred_push_directory_settings_list, DISPLAYLIST_ generic_deferred_push(deferred_push_privacy_settings_list, DISPLAYLIST_PRIVACY_SETTINGS_LIST) generic_deferred_push(deferred_push_audio_settings_list, DISPLAYLIST_AUDIO_SETTINGS_LIST) generic_deferred_push(deferred_push_input_settings_list, DISPLAYLIST_INPUT_SETTINGS_LIST) +generic_deferred_push(deferred_push_latency_settings_list, DISPLAYLIST_LATENCY_SETTINGS_LIST) generic_deferred_push(deferred_push_recording_settings_list, DISPLAYLIST_RECORDING_SETTINGS_LIST) generic_deferred_push(deferred_push_playlist_settings_list, DISPLAYLIST_PLAYLIST_SETTINGS_LIST) generic_deferred_push(deferred_push_input_hotkey_binds_list, DISPLAYLIST_INPUT_HOTKEY_BINDS_LIST) @@ -808,6 +809,11 @@ static int menu_cbs_init_bind_deferred_push_compare_label( { BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_audio_settings_list); } + else if (strstr(label, + msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_LATENCY_SETTINGS_LIST))) + { + BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_latency_settings_list); + } else if (strstr(label, msg_hash_to_str(MENU_ENUM_LABEL_CORE_INFORMATION))) { @@ -1158,6 +1164,9 @@ static int menu_cbs_init_bind_deferred_push_compare_label( case MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST: BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_audio_settings_list); break; + case MENU_ENUM_LABEL_DEFERRED_LATENCY_SETTINGS_LIST: + BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_latency_settings_list); + break; case MENU_ENUM_LABEL_DEFERRED_CORE_SETTINGS_LIST: BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_core_settings_list); break; diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index f0296a7ced..ff85e2c327 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -251,6 +251,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MENU_ENUM_LABEL_DEFERRED_ACCOUNTS_LIST; case ACTION_OK_DL_INPUT_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_INPUT_SETTINGS_LIST; + case ACTION_OK_DL_LATENCY_SETTINGS_LIST: + return MENU_ENUM_LABEL_DEFERRED_LATENCY_SETTINGS_LIST; case ACTION_OK_DL_DRIVER_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_DRIVER_SETTINGS_LIST; case ACTION_OK_DL_CORE_SETTINGS_LIST: @@ -798,6 +800,7 @@ int generic_action_ok_displaylist_push(const char *path, break; case ACTION_OK_DL_ACCOUNTS_LIST: case ACTION_OK_DL_INPUT_SETTINGS_LIST: + case ACTION_OK_DL_LATENCY_SETTINGS_LIST: case ACTION_OK_DL_DRIVER_SETTINGS_LIST: case ACTION_OK_DL_CORE_SETTINGS_LIST: case ACTION_OK_DL_VIDEO_SETTINGS_LIST: @@ -3219,6 +3222,7 @@ default_action_ok_func(action_ok_push_configuration_settings_list, ACTION_OK_DL_ default_action_ok_func(action_ok_push_core_settings_list, ACTION_OK_DL_CORE_SETTINGS_LIST) default_action_ok_func(action_ok_push_audio_settings_list, ACTION_OK_DL_AUDIO_SETTINGS_LIST) default_action_ok_func(action_ok_push_input_settings_list, ACTION_OK_DL_INPUT_SETTINGS_LIST) +default_action_ok_func(action_ok_push_latency_settings_list, ACTION_OK_DL_LATENCY_SETTINGS_LIST) default_action_ok_func(action_ok_push_recording_settings_list, ACTION_OK_DL_RECORDING_SETTINGS_LIST) default_action_ok_func(action_ok_push_playlist_settings_list, ACTION_OK_DL_PLAYLIST_SETTINGS_LIST) default_action_ok_func(action_ok_push_input_hotkey_binds_list, ACTION_OK_DL_INPUT_HOTKEY_BINDS_LIST) @@ -4218,6 +4222,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_SETTINGS: BIND_ACTION_OK(cbs, action_ok_push_audio_settings_list); break; + case MENU_ENUM_LABEL_LATENCY_SETTINGS: + BIND_ACTION_OK(cbs, action_ok_push_latency_settings_list); + break; case MENU_ENUM_LABEL_CORE_SETTINGS: BIND_ACTION_OK(cbs, action_ok_push_core_settings_list); break; diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 1f59d48752..d9841a6294 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -79,6 +79,7 @@ default_sublabel_macro(action_bind_sublabel_suspend_screensaver_enable, MENU_ default_sublabel_macro(action_bind_sublabel_video_window_scale, MENU_ENUM_SUBLABEL_VIDEO_WINDOW_SCALE) default_sublabel_macro(action_bind_sublabel_audio_settings_list, MENU_ENUM_SUBLABEL_AUDIO_SETTINGS) default_sublabel_macro(action_bind_sublabel_input_settings_list, MENU_ENUM_SUBLABEL_INPUT_SETTINGS) +default_sublabel_macro(action_bind_sublabel_latency_settings_list, MENU_ENUM_SUBLABEL_LATENCY_SETTINGS) default_sublabel_macro(action_bind_sublabel_wifi_settings_list, MENU_ENUM_SUBLABEL_WIFI_SETTINGS) default_sublabel_macro(action_bind_sublabel_netplay_lan_scan_settings_list,MENU_ENUM_SUBLABEL_NETPLAY_LAN_SCAN_SETTINGS) default_sublabel_macro(action_bind_sublabel_help_list, MENU_ENUM_SUBLABEL_HELP_LIST) @@ -1501,6 +1502,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_settings_list); break; + case MENU_ENUM_LABEL_LATENCY_SETTINGS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_latency_settings_list); + break; case MENU_ENUM_LABEL_RECORDING_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_recording_settings_list); break; diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index c5f99e6dc6..0396b7826c 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -125,6 +125,7 @@ default_title_macro(action_get_privacy_settings_list, MENU_ENUM_LABEL_ default_title_macro(action_get_updater_settings_list, MENU_ENUM_LABEL_VALUE_UPDATER_SETTINGS) default_title_macro(action_get_audio_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_SETTINGS) default_title_macro(action_get_input_settings_list, MENU_ENUM_LABEL_VALUE_INPUT_SETTINGS) +default_title_macro(action_get_latency_settings_list, MENU_ENUM_LABEL_VALUE_LATENCY_SETTINGS) default_title_macro(action_get_core_cheat_options_list, MENU_ENUM_LABEL_VALUE_CORE_CHEAT_OPTIONS) default_title_macro(action_get_load_content_list, MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_LIST) default_title_macro(action_get_load_content_special, MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_SPECIAL) @@ -475,6 +476,11 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs, BIND_ACTION_GET_TITLE(cbs, action_get_audio_settings_list); return 0; } + else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_LATENCY_SETTINGS_LIST))) + { + BIND_ACTION_GET_TITLE(cbs, action_get_latency_settings_list); + return 0; + } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SYSTEM_INFORMATION))) { BIND_ACTION_GET_TITLE(cbs, action_get_system_information_list); diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index e28dc238e9..60e58e04e6 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -2728,6 +2728,8 @@ static void materialui_list_insert(void *userdata, || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_INPUT_SETTINGS)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_LATENCY_SETTINGS)) + || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_INPUT_HOTKEY_BINDS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CORE_SETTINGS)) diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 864b8f06fa..b0fff984c9 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -68,6 +68,7 @@ enum ACTION_OK_DL_DRIVER_SETTINGS_LIST, ACTION_OK_DL_VIDEO_SETTINGS_LIST, ACTION_OK_DL_AUDIO_SETTINGS_LIST, + ACTION_OK_DL_LATENCY_SETTINGS_LIST, ACTION_OK_DL_CONFIGURATION_SETTINGS_LIST, ACTION_OK_DL_SAVING_SETTINGS_LIST, ACTION_OK_DL_LOGGING_SETTINGS_LIST, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 229a2392ea..a26e2e03c6 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5005,17 +5005,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_SLOWMOTION_RATIO, PARSE_ONLY_FLOAT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_RUN_AHEAD_ENABLED, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, - PARSE_ONLY_UINT, false); -#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, - PARSE_ONLY_BOOL, false); -#endif if (settings->bools.menu_show_advanced_settings) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE, @@ -5932,18 +5921,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_SWAP_INTERVAL, PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_VIDEO_HARD_SYNC, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_VIDEO_HARD_SYNC_FRAMES, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_VIDEO_FRAME_DELAY, - PARSE_ONLY_UINT, false); menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_BLACK_FRAME_INSERTION, PARSE_ONLY_BOOL, false); @@ -6004,9 +5981,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_AUDIO_SYNC, PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_AUDIO_LATENCY, - PARSE_ONLY_UINT, false); menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_AUDIO_RESAMPLER_QUALITY, PARSE_ONLY_UINT, false); @@ -6056,9 +6030,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_UNIFIED_MENU_CONTROLS, PARSE_ONLY_BOOL, false); - ret = menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_INPUT_POLL_TYPE_BEHAVIOR, - PARSE_ONLY_UINT, false); ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_ICADE_ENABLE, PARSE_ONLY_BOOL, false); @@ -6111,6 +6082,41 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) } } + info->need_refresh = true; + info->need_push = true; + break; + case DISPLAYLIST_LATENCY_SETTINGS_LIST: + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES, + PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_HARD_SYNC, + PARSE_ONLY_BOOL, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_HARD_SYNC_FRAMES, + PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_FRAME_DELAY, + PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_AUDIO_LATENCY, + PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_INPUT_POLL_TYPE_BEHAVIOR, + PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_RUN_AHEAD_ENABLED, + PARSE_ONLY_BOOL, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, + PARSE_ONLY_UINT, false); +#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, + PARSE_ONLY_BOOL, false); +#endif + info->need_refresh = true; info->need_push = true; break; @@ -6124,6 +6130,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_AUDIO_SETTINGS, PARSE_ACTION, false); ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_SETTINGS, PARSE_ACTION, false); + ret = menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_LATENCY_SETTINGS, PARSE_ACTION, false); ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CORE_SETTINGS, PARSE_ACTION, false); ret = menu_displaylist_parse_settings_enum(menu, info, diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 7bcd105959..3147c9f62a 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -120,6 +120,7 @@ enum menu_displaylist_ctl_state DISPLAYLIST_AUDIO_SETTINGS_LIST, DISPLAYLIST_CORE_SETTINGS_LIST, DISPLAYLIST_INPUT_SETTINGS_LIST, + DISPLAYLIST_LATENCY_SETTINGS_LIST, DISPLAYLIST_INPUT_HOTKEY_BINDS_LIST, DISPLAYLIST_ONSCREEN_OVERLAY_SETTINGS_LIST, DISPLAYLIST_ONSCREEN_DISPLAY_SETTINGS_LIST, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 65b5ae003f..4f8b30e6ad 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -2251,6 +2251,14 @@ static bool setting_append_list( &subgroup_info, parent_group); + CONFIG_ACTION( + list, list_info, + MENU_ENUM_LABEL_LATENCY_SETTINGS, + MENU_ENUM_LABEL_VALUE_LATENCY_SETTINGS, + &group_info, + &subgroup_info, + parent_group); + CONFIG_ACTION( list, list_info, MENU_ENUM_LABEL_CORE_SETTINGS, diff --git a/msg_hash.h b/msg_hash.h index 7070f56a4d..1ebe02b5a7 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -976,6 +976,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_DEFERRED_RECORDING_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_PLAYLIST_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_INPUT_SETTINGS_LIST, + MENU_ENUM_LABEL_DEFERRED_LATENCY_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_DRIVER_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_VIDEO_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_CONFIGURATION_SETTINGS_LIST, @@ -1477,6 +1478,7 @@ enum msg_hash_enums MENU_LABEL(DRIVER_SETTINGS), MENU_LABEL(VIDEO_SETTINGS), MENU_LABEL(AUDIO_SETTINGS), + MENU_LABEL(LATENCY_SETTINGS), MENU_LABEL(CORE_SETTINGS), MENU_LABEL(CONFIGURATION_SETTINGS), MENU_LABEL(LOGGING_SETTINGS), From c4f818599cdb07310095aecc2b9dffe27731eade Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Mon, 16 Apr 2018 16:23:59 -0500 Subject: [PATCH 330/517] Add some fbdev refresh rate polls. Also Xv. --- gfx/drivers/omap_gfx.c | 12 +++++++++++- gfx/drivers/sunxi_gfx.c | 11 +++++++++++ gfx/drivers/xshm_gfx.c | 7 +------ gfx/drivers/xvideo.c | 27 ++++++++++++++++++++++++++- gfx/drivers_context/mali_fbdev_ctx.c | 15 ++++++++++++++- 5 files changed, 63 insertions(+), 9 deletions(-) diff --git a/gfx/drivers/omap_gfx.c b/gfx/drivers/omap_gfx.c index 9c650f27a0..fc7bc1fd21 100644 --- a/gfx/drivers/omap_gfx.c +++ b/gfx/drivers/omap_gfx.c @@ -1129,13 +1129,23 @@ static void omap_gfx_set_texture_enable(void *data, bool state, bool full_screen (void) full_screen; } +static float omap_get_refresh_rate(void *data) +{ + omap_video_t *vid = (omap_video_t*)data; + struct fb_var_screeninfo *s = &vid->omap->current_state->si; + + return 1000000.0f / s->pixclock / + (s->xres + s->left_margin + s->right_margin + s->hsync_len) * 1000000.0f / + (s->yres + s->upper_margin + s->lower_margin + s->vsync_len); +} + static const video_poke_interface_t omap_gfx_poke_interface = { NULL, /* set_coords */ NULL, /* set_mvp */ NULL, NULL, NULL, - NULL, /* get_refresh_rate */ + omap_get_refresh_rate, NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/sunxi_gfx.c b/gfx/drivers/sunxi_gfx.c index 9d6e786deb..d2336eaaf8 100644 --- a/gfx/drivers/sunxi_gfx.c +++ b/gfx/drivers/sunxi_gfx.c @@ -109,6 +109,7 @@ typedef struct uint32_t framebuffer_size; /* total size of the framebuffer */ int framebuffer_height;/* virtual vertical resolution */ uint32_t gfx_layer_size; /* the size of the primary layer */ + float refresh_rate; /* Layers support */ int gfx_layer_id; @@ -416,6 +417,9 @@ static sunxi_disp_t *sunxi_disp_init(const char *device) ctx->framebuffer_height = ctx->framebuffer_size / (ctx->xres * ctx->bits_per_pixel / 8); ctx->gfx_layer_size = ctx->xres * ctx->yres * fb_var.bits_per_pixel / 8; + ctx->refresh_rate = 1000000.0f / fb_var.pixclock * 1000000.0f / + (fb_var.yres + fb_var.upper_margin + fb_var.lower_margin + fb_var.vsync_len) + (fb_var.xres + fb_var.left_margin + fb_var.right_margin + fb_var.hsync_len); if (ctx->framebuffer_size < ctx->gfx_layer_size) { @@ -931,6 +935,13 @@ static void sunxi_set_aspect_ratio (void *data, unsigned aspect_ratio_idx) } } +static float sunxi_get_refresh_rate (void *data) +{ + struct sunxi_video *_dispvars = (struct sunxi_video*)data; + + return _dispvars->sunxi_disp->refresh_rate; +} + static const video_poke_interface_t sunxi_poke_interface = { NULL, /* set_coords */ NULL, /* set_mvp */ diff --git a/gfx/drivers/xshm_gfx.c b/gfx/drivers/xshm_gfx.c index 6385f591ed..7f6ce812f8 100644 --- a/gfx/drivers/xshm_gfx.c +++ b/gfx/drivers/xshm_gfx.c @@ -203,18 +203,13 @@ static void xshm_grab_mouse_toggle(void *data) } -static float xshm_poke_get_refresh_rate(void *data) -{ - return x11_get_refresh_rate(data); -} - static video_poke_interface_t xshm_video_poke_interface = { NULL, /* set_coords */ NULL, /* set_mvp */ NULL, NULL, NULL, - xshm_poke_get_refresh_rate, + x11_get_refresh_rate, xshm_poke_set_filtering, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/xvideo.c b/gfx/drivers/xvideo.c index 94e81e6f87..bb6f1dc6e9 100644 --- a/gfx/drivers/xvideo.c +++ b/gfx/drivers/xvideo.c @@ -933,11 +933,36 @@ static bool xv_read_viewport(void *data, uint8_t *buffer, bool is_idle) return true; } +static video_poke_interface_t xv_video_poke_interface = { + NULL, + NULL, + NULL, + NULL, + NULL, + x11_get_refresh_rate, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + static void xv_get_poke_interface(void *data, const video_poke_interface_t **iface) { (void)data; - (void)iface; + *iface = &xv_video_poke_interface; } static bool xv_set_shader(void *data, diff --git a/gfx/drivers_context/mali_fbdev_ctx.c b/gfx/drivers_context/mali_fbdev_ctx.c index b30de94dcf..b2ef7917c8 100644 --- a/gfx/drivers_context/mali_fbdev_ctx.c +++ b/gfx/drivers_context/mali_fbdev_ctx.c @@ -50,6 +50,7 @@ typedef struct } native_window; bool resize; unsigned width, height; + float refresh_rate; } mali_ctx_data_t; static enum gfx_ctx_api mali_api = GFX_CTX_NONE; @@ -178,6 +179,11 @@ static bool gfx_ctx_mali_fbdev_set_video_mode(void *data, mali->native_window.width = vinfo.xres; mali->native_window.height = vinfo.yres; + mali->refresh_rate = + ctx->refresh_rate = 1000000.0f / vinfo.pixclock * 1000000.0f / + (vinfo.yres + vinfo.upper_margin + vinfo.lower_margin + vinfo.vsync_len) + (vinfo.xres + vinfo.left_margin + vinfo.right_margin + vinfo.hsync_len); + #ifdef HAVE_EGL if (!egl_create_context(&mali->egl, attribs)) { @@ -286,6 +292,13 @@ static void gfx_ctx_mali_fbdev_set_flags(void *data, uint32_t flags) (void)data; } +static float gfx_ctx_mali_fbdev_get_refresh_rate(void *data) +{ + mali_ctx_data_t *mali = (mali_ctx_data_t*)data; + + return mali->refresh_rate; +} + const gfx_ctx_driver_t gfx_ctx_mali_fbdev = { gfx_ctx_mali_fbdev_init, gfx_ctx_mali_fbdev_destroy, @@ -294,7 +307,7 @@ const gfx_ctx_driver_t gfx_ctx_mali_fbdev = { gfx_ctx_mali_fbdev_set_swap_interval, gfx_ctx_mali_fbdev_set_video_mode, gfx_ctx_mali_fbdev_get_video_size, - NULL, /* get_refresh_rate */ + gfx_ctx_mali_fbdev_get_refresh_rate, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ From 6aa33c871094382816abf32e26d704d62167b275 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Mon, 16 Apr 2018 16:56:36 -0500 Subject: [PATCH 331/517] Add some X11 error checking. --- gfx/common/x11_common.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index 97ec6adb08..e7b5172d6a 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -66,7 +66,7 @@ Display *g_x11_dpy = NULL; unsigned g_x11_screen = 0; Colormap g_x11_cmap; -Window g_x11_win; +Window g_x11_win = None; static Atom XA_NET_WM_STATE; static Atom XA_NET_WM_STATE_FULLSCREEN; @@ -242,7 +242,12 @@ float x11_get_refresh_rate(void *data) int screenid; int dotclock; - XGetWindowAttributes(g_x11_dpy, g_x11_win, &attr); + if (!g_x11_dpy || g_x11_win == None) + return 0.0f; + + if (!XGetWindowAttributes(g_x11_dpy, g_x11_win, &attr)) + return 0.0f; + screen = attr.screen; screenid = XScreenNumberOfScreen(screen); From 9933eb004bff091682777ea12ac2ff86a2e0d8f8 Mon Sep 17 00:00:00 2001 From: meleu Date: Mon, 16 Apr 2018 20:23:59 -0300 Subject: [PATCH 332/517] CHANGES.md: fixed NGP cheevos incompatibilities --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index fa9879b423..b538863ba3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ - COMMON: Added runahead system; allows you to drive down latency even further. - CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. +- CHEEVOS: Fixed incompatibilities with Neo Geo Pocket achievement sets. - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. - D3D11/D3D12: Fix crashes with completely black or white thumbnail textures in XMB. - LIBRETRO: Addition - Functions to enable and disable audio and video, and an environment function to query status of audio and video enables. From 3f8ea809de6ac176c1d55cdfc5e44c8bab66f8a5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 17 Apr 2018 03:14:20 +0200 Subject: [PATCH 333/517] (360) Buildfix --- gfx/video_crt_switch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index 3b906fcdf3..618ee7ecd7 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -20,7 +20,7 @@ #include #include -#if defined(_WIN32) +#if defined(_WIN32) && !defined(_XBOX) #include #endif @@ -51,7 +51,7 @@ static void crt_check_first_run(void) * at some point in time or should it stay like this? */ /* Run of first boot to get current display resolution */ -#if defined(_WIN32) +#if defined(_WIN32) && !defined(_XBOX) orig_height = GetSystemMetrics(SM_CYSCREEN); orig_width = GetSystemMetrics(SM_CXSCREEN); #endif From f050104740bfbd88765a36b25cd17772047b7504 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 17 Apr 2018 03:19:35 +0200 Subject: [PATCH 334/517] Change return type of content_get_subsystem_rom_id --- content.h | 7 +++---- tasks/task_content.c | 24 ++++++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/content.h b/content.h index ad4211c5a0..5e25e47c64 100644 --- a/content.h +++ b/content.h @@ -92,17 +92,16 @@ void content_clear_subsystem(void); void content_set_subsystem(unsigned subsystem); /* Get the current subsystem*/ -int content_get_subsystem(); +int content_get_subsystem(void); /* Add a rom to the subsystem rom buffer */ void content_add_subsystem(const char* path); /* Get the current subsystem rom id */ -int content_get_subsystem_rom_id(); +unsigned content_get_subsystem_rom_id(void); /* Set environment variables before a subsystem load */ -void content_set_subsystem_info(); - +void content_set_subsystem_info(void); RETRO_END_DECLS diff --git a/tasks/task_content.c b/tasks/task_content.c index 6d0b9a9c5f..3676308d73 100644 --- a/tasks/task_content.c +++ b/tasks/task_content.c @@ -144,7 +144,7 @@ static uint32_t content_rom_crc = 0; static bool pending_subsystem_init = false; static int pending_subsystem_rom_num = 0; static int pending_subsystem_id = 0; -static int pending_subsystem_rom_id = 0; +static unsigned pending_subsystem_rom_id = 0; static char pending_subsystem_ident[255]; static char pending_subsystem_extensions[PATH_MAX_LENGTH]; @@ -1741,9 +1741,11 @@ void content_get_status( /* Clears the pending subsystem rom buffer*/ void content_clear_subsystem(void) { - int i; + unsigned i; + pending_subsystem_rom_id = 0; - pending_subsystem_init = false; + pending_subsystem_init = false; + for (i = 0; i < RARCH_MAX_SUBSYSTEM_ROMS; i++) { if (pending_subsystem_roms[i]) @@ -1763,15 +1765,17 @@ int content_get_subsystem() /* Set the current subsystem*/ void content_set_subsystem(unsigned idx) { - rarch_system_info_t *system = runloop_get_system_info(); - const struct retro_subsystem_info* subsystem = NULL; + rarch_system_info_t *system = runloop_get_system_info(); + const struct retro_subsystem_info *subsystem = system ? + system->subsystem.data + pending_subsystem_id : NULL; - subsystem = system->subsystem.data + pending_subsystem_id; + pending_subsystem_id = idx; - pending_subsystem_id = idx; + strlcpy(pending_subsystem_ident, + subsystem->ident, sizeof(pending_subsystem_ident)); + + pending_subsystem_rom_num = subsystem->num_roms; - strlcpy(pending_subsystem_ident, subsystem->ident, sizeof(pending_subsystem_ident)); - pending_subsystem_rom_num = subsystem->num_roms; RARCH_LOG("[subsystem] settings current subsytem to: %d(%s) roms: %d\n", pending_subsystem_id, pending_subsystem_ident, pending_subsystem_rom_num); } @@ -1790,7 +1794,7 @@ void content_add_subsystem(const char* path) } /* Get the current subsystem rom id */ -int content_get_subsystem_rom_id(void) +unsigned content_get_subsystem_rom_id(void) { return pending_subsystem_rom_id; } From d7d5dfdc73350b1f2f07070dc204741ea770c43a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 17 Apr 2018 03:37:56 +0200 Subject: [PATCH 335/517] Add display_server_get_current_resolution --- gfx/display_servers/dispserv_null.c | 1 + gfx/display_servers/dispserv_win32.c | 60 ++++++++++++++++++---------- gfx/display_servers/dispserv_x11.c | 1 + gfx/video_crt_switch.c | 13 +----- gfx/video_display_server.c | 10 +++++ gfx/video_display_server.h | 4 ++ 6 files changed, 55 insertions(+), 34 deletions(-) diff --git a/gfx/display_servers/dispserv_null.c b/gfx/display_servers/dispserv_null.c index 65d46c5d12..8de3b98710 100644 --- a/gfx/display_servers/dispserv_null.c +++ b/gfx/display_servers/dispserv_null.c @@ -50,6 +50,7 @@ const video_display_server_t dispserv_null = { null_set_window_progress, NULL, NULL, + NULL, "null" }; diff --git a/gfx/display_servers/dispserv_win32.c b/gfx/display_servers/dispserv_win32.c index 901b500a46..6bbc067dcd 100644 --- a/gfx/display_servers/dispserv_win32.c +++ b/gfx/display_servers/dispserv_win32.c @@ -67,8 +67,8 @@ be received by your application before it calls any ITaskbarList3 method. static void* win32_display_server_init(void) { - dispserv_win32_t *dispserv = (dispserv_win32_t*)calloc(1, sizeof(*dispserv)); HRESULT hr; + dispserv_win32_t *dispserv = (dispserv_win32_t*)calloc(1, sizeof(*dispserv)); (void)hr; @@ -77,11 +77,13 @@ static void* win32_display_server_init(void) #ifdef HAS_TASKBAR_EXT #ifdef __cplusplus - /* when compiling in C++ mode, GUIDs are references instead of pointers */ - hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void**)&g_taskbarList); + /* When compiling in C++ mode, GUIDs are references instead of pointers */ + hr = CoCreateInstance(CLSID_TaskbarList, NULL, + CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void**)&g_taskbarList); #else - /* mingw GUIDs are pointers instead of references since we're in C mode */ - hr = CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, (void**)&g_taskbarList); + /* Mingw GUIDs are pointers instead of references since we're in C mode */ + hr = CoCreateInstance(&CLSID_TaskbarList, NULL, + CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, (void**)&g_taskbarList); #endif if (!SUCCEEDED(hr)) @@ -115,7 +117,8 @@ static bool win32_set_window_opacity(void *data, unsigned opacity) HWND hwnd = win32_get_window(); dispserv_win32_t *serv = (dispserv_win32_t*)data; - serv->opacity = opacity; + if (serv) + serv->opacity = opacity; #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 /* Set window transparency on Windows 2000 and above */ @@ -126,15 +129,14 @@ static bool win32_set_window_opacity(void *data, unsigned opacity) GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); return SetLayeredWindowAttributes(hwnd, 0, (255 * opacity) / 100, LWA_ALPHA); } - else - { - SetWindowLongPtr(hwnd, - GWL_EXSTYLE, - GetWindowLongPtr(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED); - return true; - } -#endif + + SetWindowLongPtr(hwnd, + GWL_EXSTYLE, + GetWindowLongPtr(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED); + return true; +#else return false; +#endif } static bool win32_set_window_progress(void *data, int progress, bool finished) @@ -151,7 +153,8 @@ static bool win32_set_window_progress(void *data, int progress, bool finished) if (progress == -1) { - if (ITaskbarList3_SetProgressState(g_taskbarList, hwnd, TBPF_INDETERMINATE) == S_OK) + if (ITaskbarList3_SetProgressState( + g_taskbarList, hwnd, TBPF_INDETERMINATE) == S_OK) ret = true; if (!ret) @@ -159,7 +162,8 @@ static bool win32_set_window_progress(void *data, int progress, bool finished) } else if (finished) { - if (ITaskbarList3_SetProgressState(g_taskbarList, hwnd, TBPF_NOPROGRESS) == S_OK) + if (ITaskbarList3_SetProgressState( + g_taskbarList, hwnd, TBPF_NOPROGRESS) == S_OK) ret = true; if (!ret) @@ -167,13 +171,15 @@ static bool win32_set_window_progress(void *data, int progress, bool finished) } else if (progress >= 0) { - if (ITaskbarList3_SetProgressState(g_taskbarList, hwnd, TBPF_NORMAL) == S_OK) + if (ITaskbarList3_SetProgressState( + g_taskbarList, hwnd, TBPF_NORMAL) == S_OK) ret = true; if (!ret) return false; - if (ITaskbarList3_SetProgressValue(g_taskbarList, hwnd, progress, 100) == S_OK) + if (ITaskbarList3_SetProgressValue( + g_taskbarList, hwnd, progress, 100) == S_OK) ret = true; } #endif @@ -202,9 +208,9 @@ static bool win32_display_server_set_resolution(void *data, DEVMODE devmode; int iModeNum; - int freq = 0; - DWORD flags = 0; - int depth = 0; + int freq = 0; + DWORD flags = 0; + int depth = 0; dispserv_win32_t *serv = (dispserv_win32_t*)data; if (!serv) @@ -215,7 +221,7 @@ static bool win32_display_server_set_resolution(void *data, EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); - /* used to stop superresolution bug */ + /* Used to stop super resolution bug */ if (width == curDevmode.dmPelsWidth) width = 0; if (width == 0) @@ -273,12 +279,22 @@ static bool win32_display_server_set_resolution(void *data, return true; } +void win32_display_server_get_current_resolution( + unsigned *width, unsigned *height) +{ + if (width) + *width = GetSystemMetrics(SM_CYSCREEN); + if (height) + *height = GetSystemMetrics(SM_CXSCREEN); +} + const video_display_server_t dispserv_win32 = { win32_display_server_init, win32_display_server_destroy, win32_set_window_opacity, win32_set_window_progress, win32_set_window_decorations, + win32_display_server_get_current_resolution, win32_display_server_set_resolution, "win32" }; diff --git a/gfx/display_servers/dispserv_x11.c b/gfx/display_servers/dispserv_x11.c index c9ce9293b8..ee19ec0478 100644 --- a/gfx/display_servers/dispserv_x11.c +++ b/gfx/display_servers/dispserv_x11.c @@ -78,6 +78,7 @@ const video_display_server_t dispserv_x11 = { x11_set_window_opacity, NULL, x11_set_window_decorations, + NULL, /* get_current_resolution */ NULL, /* set_resolution */ "x11" }; diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c index 618ee7ecd7..754776ca09 100644 --- a/gfx/video_crt_switch.c +++ b/gfx/video_crt_switch.c @@ -20,10 +20,6 @@ #include #include -#if defined(_WIN32) && !defined(_XBOX) -#include -#endif - #include "video_driver.h" #include "video_crt_switch.h" #include "video_display_server.h" @@ -47,14 +43,7 @@ static void crt_check_first_run(void) if (!first_run) return; - /* TODO/FIXME - do we want to set first_run back to true - * at some point in time or should it stay like this? */ - - /* Run of first boot to get current display resolution */ -#if defined(_WIN32) && !defined(_XBOX) - orig_height = GetSystemMetrics(SM_CYSCREEN); - orig_width = GetSystemMetrics(SM_CXSCREEN); -#endif + video_display_server_get_current_resolution(&orig_width, &orig_height); first_run = false; } diff --git a/gfx/video_display_server.c b/gfx/video_display_server.c index 296a10e264..1229720896 100644 --- a/gfx/video_display_server.c +++ b/gfx/video_display_server.c @@ -82,6 +82,16 @@ bool video_display_server_set_window_decorations(bool on) return false; } +bool video_display_server_get_current_resolution(unsigned *width, unsigned *height) +{ + if (current_display_server && current_display_server->get_current_resolution) + { + current_display_server->get_current_resolution(width, height); + return true; + } + return false; +} + bool video_display_server_switch_resolution(unsigned width, unsigned height, int f_restore, int hz) { diff --git a/gfx/video_display_server.h b/gfx/video_display_server.h index 365223b30a..5e2fda7c77 100644 --- a/gfx/video_display_server.h +++ b/gfx/video_display_server.h @@ -30,6 +30,7 @@ typedef struct video_display_server bool (*set_window_opacity)(void *data, unsigned opacity); bool (*set_window_progress)(void *data, int progress, bool finished); bool (*set_window_decorations)(void *data, bool on); + void (*get_current_resolution)(unsigned *width, unsigned *height); bool (*switch_resolution)(void *data, unsigned width, unsigned height, int f_restore, int hz); const char *ident; @@ -43,6 +44,9 @@ bool video_display_server_set_window_opacity(unsigned opacity); bool video_display_server_set_window_progress(int progress, bool finished); +bool video_display_server_get_current_resolution( + unsigned *width, unsigned *height); + bool video_display_server_set_window_decorations(bool on); bool video_display_server_switch_resolution( From 3755003d333b003087f5845521226132bdce6da1 Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 16 Apr 2018 21:26:43 -0500 Subject: [PATCH 336/517] remap-redux: unify menu displaylist --- menu/menu_displaylist.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index a26e2e03c6..16da491e9c 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3278,7 +3278,6 @@ static int menu_displaylist_parse_options_remappings( if (device == RETRO_DEVICE_JOYPAD || device == RETRO_DEVICE_ANALOG) { - /* change to RARCH_FIRST_CUSTOM_BIND + 8 once analog remapping is implemented */ for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND + 8; retro_id++) { char desc_label[64]; @@ -3308,19 +3307,7 @@ static int menu_displaylist_parse_options_remappings( (p * (RARCH_FIRST_CUSTOM_BIND + 8)) + retro_id, 0, 0); } } - } - } - if (system) - { - settings_t *settings = config_get_ptr(); - unsigned device; - - for (p = 0; p < MAX_USERS; p++) - { - device = settings->uints.input_libretro_device[p]; - device &= RETRO_DEVICE_MASK; - - if (device == RETRO_DEVICE_KEYBOARD) + else if (device == RETRO_DEVICE_KEYBOARD) { for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) { From 9f2b56a929a2acf9359a32335ef10b0312724f95 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 03:27:41 +0200 Subject: [PATCH 337/517] Update Direct3D 10 driver --- Makefile.common | 4 +- gfx/common/d3d10_common.c | 78 +- gfx/common/d3d10_common.h | 177 ++- gfx/common/d3d11_common.c | 13 +- gfx/common/d3d11_common.h | 1 + gfx/drivers/d3d10.c | 1341 ++++++++++++++++++--- gfx/drivers/d3d11.c | 1 + gfx/drivers_font/d3d10_font.c | 379 ++++++ gfx/font_driver.c | 36 + gfx/font_driver.h | 1 + gfx/video_defines.h | 1 + griffin/griffin.c | 8 + menu/drivers_display/menu_display_d3d10.c | 289 +++++ menu/menu_driver.c | 4 + menu/menu_driver.h | 1 + 15 files changed, 2173 insertions(+), 161 deletions(-) create mode 100644 gfx/drivers_font/d3d10_font.c create mode 100644 menu/drivers_display/menu_display_d3d10.c diff --git a/Makefile.common b/Makefile.common index bbd69c3a23..5de5f3d9b9 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1267,7 +1267,9 @@ endif ifeq ($(HAVE_D3D10), 1) OBJ += gfx/drivers/d3d10.o \ - gfx/common/d3d10_common.o + gfx/common/d3d10_common.o \ + gfx/drivers_font/d3d10_font.o \ + menu/drivers_display/menu_display_d3d10.o DEFINES += -DHAVE_D3D10 endif diff --git a/gfx/common/d3d10_common.c b/gfx/common/d3d10_common.c index 45d364d08f..12adaf5493 100644 --- a/gfx/common/d3d10_common.c +++ b/gfx/common/d3d10_common.c @@ -18,6 +18,7 @@ #include #include "d3d10_common.h" +#include "d3dcompiler_common.h" #ifdef HAVE_DYNAMIC #include @@ -104,6 +105,7 @@ void d3d10_init_texture(D3D10Device device, d3d10_texture_t* texture) } void d3d10_update_texture( + D3D10Device ctx, int width, int height, int pitch, @@ -112,8 +114,13 @@ void d3d10_update_texture( d3d10_texture_t* texture) { D3D10_MAPPED_TEXTURE2D mapped_texture; + D3D10_BOX frame_box = { 0, 0, 0, width, height, 1 }; - D3D10MapTexture2D(texture->staging, 0, D3D10_MAP_WRITE, 0, + if (!texture || !texture->staging) + return; + + D3D10MapTexture2D(texture->staging, + 0, D3D10_MAP_WRITE, 0, &mapped_texture); #if 0 @@ -130,8 +137,11 @@ void d3d10_update_texture( D3D10UnmapTexture2D(texture->staging, 0); - if (texture->desc.Usage == D3D10_USAGE_DEFAULT) - texture->dirty = true; + D3D10CopyTexture2DSubresourceRegion( + ctx, texture->handle, 0, 0, 0, 0, texture->staging, 0, &frame_box); + + if (texture->desc.MiscFlags & D3D10_RESOURCE_MISC_GENERATE_MIPS) + D3D10GenerateMips(ctx, texture->view); } DXGI_FORMAT @@ -155,3 +165,65 @@ d3d10_get_closest_match(D3D10Device device, assert(*format); return *format; } + +bool d3d10_init_shader( + D3D10Device device, + const char* src, + size_t size, + const void* src_name, + LPCSTR vs_entry, + LPCSTR ps_entry, + LPCSTR gs_entry, + const D3D10_INPUT_ELEMENT_DESC* input_element_descs, + UINT num_elements, + d3d10_shader_t* out) +{ + D3DBlob vs_code = NULL; + D3DBlob ps_code = NULL; + D3DBlob gs_code = NULL; + + bool success = true; + + if (!src) /* LPCWSTR filename */ + { + if (vs_entry && !d3d_compile_from_file((LPCWSTR)src_name, vs_entry, "vs_5_0", &vs_code)) + success = false; + if (ps_entry && !d3d_compile_from_file((LPCWSTR)src_name, ps_entry, "ps_5_0", &ps_code)) + success = false; + if (gs_entry && !d3d_compile_from_file((LPCWSTR)src_name, gs_entry, "gs_5_0", &gs_code)) + success = false; + } + else /* char array */ + { + if (vs_entry && !d3d_compile(src, size, (LPCSTR)src_name, vs_entry, "vs_5_0", &vs_code)) + success = false; + if (ps_entry && !d3d_compile(src, size, (LPCSTR)src_name, ps_entry, "ps_5_0", &ps_code)) + success = false; + if (gs_entry && !d3d_compile(src, size, (LPCSTR)src_name, gs_entry, "gs_5_0", &gs_code)) + success = false; + } + + if (vs_code) + D3D10CreateVertexShader( + device, D3DGetBufferPointer(vs_code), D3DGetBufferSize(vs_code), &out->vs); + + if (ps_code) + D3D10CreatePixelShader( + device, D3DGetBufferPointer(ps_code), D3DGetBufferSize(ps_code), &out->ps); + + if (gs_code) + D3D10CreateGeometryShader( + device, D3DGetBufferPointer(gs_code), D3DGetBufferSize(gs_code), &out->gs); + + if (vs_code && input_element_descs) + D3D10CreateInputLayout( + device, + (D3D10_INPUT_ELEMENT_DESC*)input_element_descs, num_elements, D3DGetBufferPointer(vs_code), + D3DGetBufferSize(vs_code), &out->layout); + + Release(vs_code); + Release(ps_code); + Release(gs_code); + + return success; +} diff --git a/gfx/common/d3d10_common.h b/gfx/common/d3d10_common.h index 500f898bd6..bff18cb40f 100644 --- a/gfx/common/d3d10_common.h +++ b/gfx/common/d3d10_common.h @@ -20,6 +20,12 @@ #include "dxgi_common.h" #include +#include "../drivers_shader/slang_process.h" + +typedef D3D10_MAPPED_TEXTURE3D D3D10_MAPPED_SUBRESOURCE; + +typedef const ID3D10SamplerState* D3D10SamplerStateRef; + typedef ID3D10InputLayout* D3D10InputLayout; typedef ID3D10RasterizerState* D3D10RasterizerState; typedef ID3D10DepthStencilState* D3D10DepthStencilState; @@ -50,6 +56,7 @@ typedef ID3D10Debug* D3D10Debug; typedef ID3D10SwitchToRef* D3D10SwitchToRef; typedef ID3D10InfoQueue* D3D10InfoQueue; + #if !defined(__cplusplus) || defined(CINTERFACE) static INLINE void D3D10SetResourceEvictionPriority(D3D10Resource resource, UINT eviction_priority) { @@ -236,6 +243,7 @@ static INLINE void D3D10SetVShader(D3D10Device device, D3D10VertexShader vertex_ { device->lpVtbl->VSSetShader(device, vertex_shader); } + static INLINE void D3D10DrawIndexed( D3D10Device device, UINT index_count, UINT start_index_location, INT base_vertex_location) { @@ -265,6 +273,28 @@ static INLINE void D3D10SetVertexBuffers( device->lpVtbl->IASetVertexBuffers( device, start_slot, num_buffers, vertex_buffers, strides, offsets); } + +static INLINE void D3D10SetVertexBuffer( + D3D10Device device_context, + UINT slot, + D3D10Buffer const vertex_buffer, + UINT stride, + UINT offset) +{ + D3D10SetVertexBuffers(device_context, slot, 1, (D3D10Buffer* const)&vertex_buffer, &stride, &offset); +} +static INLINE void D3D10SetVShaderConstantBuffer( + D3D10Device device_context, UINT slot, D3D10Buffer const constant_buffer) +{ + D3D10SetVShaderConstantBuffers(device_context, slot, 1, (ID3D10Buffer ** const)&constant_buffer); +} + +static INLINE void D3D10SetPShaderConstantBuffer( + D3D10Device device_context, UINT slot, D3D10Buffer const constant_buffer) +{ + D3D10SetPShaderConstantBuffers(device_context, slot, 1, (ID3D10Buffer** const)&constant_buffer); +} + static INLINE void D3D10SetIndexBuffer(D3D10Device device, D3D10Buffer index_buffer, DXGI_FORMAT format, UINT offset) { @@ -1047,25 +1077,79 @@ typedef struct D3D10Texture2D handle; D3D10Texture2D staging; D3D10_TEXTURE2D_DESC desc; + D3D10RenderTargetView rt_view; D3D10ShaderResourceView view; - bool dirty; - bool ignore_alpha; + D3D10SamplerStateRef sampler; + float4_t size_data; } d3d10_texture_t; +typedef struct +{ + struct + { + float x, y, w, h; + } pos; + struct + { + float u, v, w, h; + } coords; + UINT32 colors[4]; + struct + { + float scaling; + float rotation; + } params; +} d3d10_sprite_t; + +#ifndef ALIGN +#ifdef _MSC_VER +#define ALIGN(x) __declspec(align(x)) +#else +#define ALIGN(x) __attribute__((aligned(x))) +#endif +#endif + +typedef struct ALIGN(16) +{ + math_matrix_4x4 mvp; + struct + { + float width; + float height; + } OutputSize; + float time; +} d3d10_uniform_t; + +static_assert( + (!(sizeof(d3d10_uniform_t) & 0xF)), "sizeof(d3d10_uniform_t) must be a multiple of 16"); + +typedef struct d3d10_shader_t +{ + D3D10VertexShader vs; + D3D10PixelShader ps; + D3D10GeometryShader gs; + D3D10InputLayout layout; +} d3d10_shader_t; + typedef struct { unsigned cur_mon_id; DXGISwapChain swapChain; D3D10Device device; + D3D10RasterizerState state; D3D10RenderTargetView renderTargetView; - D3D10InputLayout layout; D3D10Buffer ubo; + d3d10_uniform_t ubo_values; + D3D10SamplerState samplers[RARCH_FILTER_MAX][RARCH_WRAP_MAX]; + D3D10InputLayout layout; D3D10VertexShader vs; D3D10PixelShader ps; D3D10SamplerState sampler_nearest; D3D10SamplerState sampler_linear; D3D10BlendState blend_enable; D3D10BlendState blend_disable; + D3D10BlendState blend_pipeline; + D3D10Buffer menu_pipeline_vbo; math_matrix_4x4 mvp, mvp_no_rot; struct video_viewport vp; D3D10_VIEWPORT viewport; @@ -1075,6 +1159,31 @@ typedef struct bool resize_chain; bool keep_aspect; bool resize_viewport; + bool resize_render_targets; + bool init_history; + d3d10_shader_t shaders[GFX_MAX_SHADERS]; + + struct + { + d3d10_shader_t shader; + d3d10_shader_t shader_font; + D3D10Buffer vbo; + int offset; + int capacity; + bool enabled; + } sprites; + +#ifdef HAVE_OVERLAY + struct + { + D3D10Buffer vbo; + d3d10_texture_t* textures; + bool enabled; + bool fullscreen; + int count; + } overlays; +#endif + struct { d3d10_texture_t texture; @@ -1085,18 +1194,41 @@ typedef struct } menu; struct { - d3d10_texture_t texture; + d3d10_texture_t texture[GFX_MAX_FRAME_HISTORY + 1]; D3D10Buffer vbo; D3D10Buffer ubo; D3D10SamplerState sampler; D3D10_VIEWPORT viewport; + float4_t output_size; int rotation; } frame; + + struct + { + d3d10_shader_t shader; + D3D10Buffer buffers[SLANG_CBUFFER_MAX]; + d3d10_texture_t rt; + d3d10_texture_t feedback; + D3D10_VIEWPORT viewport; + pass_semantics_t semantics; + uint32_t frame_count; + } pass[GFX_MAX_SHADERS]; + + struct video_shader* shader_preset; + d3d10_texture_t luts[GFX_MAX_TEXTURES]; } d3d10_video_t; void d3d10_init_texture(D3D10Device device, d3d10_texture_t* texture); +static INLINE void d3d10_release_texture(d3d10_texture_t* texture) +{ + Release(texture->handle); + Release(texture->staging); + Release(texture->view); + Release(texture->rt_view); +} void d3d10_update_texture( + D3D10Device ctx, int width, int height, int pitch, @@ -1107,6 +1239,26 @@ void d3d10_update_texture( DXGI_FORMAT d3d10_get_closest_match( D3D10Device device, DXGI_FORMAT desired_format, UINT desired_format_support); +bool d3d10_init_shader( + D3D10Device device, + const char* src, + size_t size, + const void* src_name, + LPCSTR vs_entry, + LPCSTR ps_entry, + LPCSTR gs_entry, + const D3D10_INPUT_ELEMENT_DESC* input_element_descs, + UINT num_elements, + d3d10_shader_t* out); + +static INLINE void d3d10_release_shader(d3d10_shader_t* shader) +{ + Release(shader->layout); + Release(shader->vs); + Release(shader->ps); + Release(shader->gs); +} + static INLINE DXGI_FORMAT d3d10_get_closest_match_texture2D(D3D10Device device, DXGI_FORMAT desired_format) { @@ -1114,3 +1266,20 @@ d3d10_get_closest_match_texture2D(D3D10Device device, DXGI_FORMAT desired_format device, desired_format, D3D10_FORMAT_SUPPORT_TEXTURE2D | D3D10_FORMAT_SUPPORT_SHADER_SAMPLE); } + +static INLINE void d3d10_set_shader(D3D10Device ctx, d3d10_shader_t* shader) +{ + D3D10SetInputLayout(ctx, shader->layout); + D3D10SetVShader(ctx, shader->vs); + D3D10SetPShader(ctx, shader->ps); + D3D10SetGShader(ctx, shader->gs); +} + +#if !defined(__cplusplus) || defined(CINTERFACE) +static INLINE void +d3d10_set_texture_and_sampler(D3D10Device ctx, UINT slot, d3d10_texture_t* texture) +{ + D3D10SetPShaderResources(ctx, slot, 1, &texture->view); + D3D10SetPShaderSamplers(ctx, slot, 1, (D3D10SamplerState*)&texture->sampler); +} +#endif diff --git a/gfx/common/d3d11_common.c b/gfx/common/d3d11_common.c index 7ae98d995d..d236f9bcfa 100644 --- a/gfx/common/d3d11_common.c +++ b/gfx/common/d3d11_common.c @@ -140,11 +140,20 @@ void d3d11_update_texture( if (!texture || !texture->staging) return; - D3D11MapTexture2D(ctx, texture->staging, 0, D3D11_MAP_WRITE, 0, &mapped_texture); + D3D11MapTexture2D(ctx, texture->staging, + 0, D3D11_MAP_WRITE, 0, &mapped_texture); +#if 0 + PERF_START(); + conv_rgb565_argb8888(mapped_texture.pData, data, width, height, + mapped_texture.RowPitch, pitch); + PERF_STOP(); +#else dxgi_copy( - width, height, format, pitch, data, texture->desc.Format, mapped_texture.RowPitch, + width, height, format, pitch, data, + texture->desc.Format, mapped_texture.RowPitch, mapped_texture.pData); +#endif D3D11UnmapTexture2D(ctx, texture->staging, 0); diff --git a/gfx/common/d3d11_common.h b/gfx/common/d3d11_common.h index 68333f1605..69128cd07a 100644 --- a/gfx/common/d3d11_common.h +++ b/gfx/common/d3d11_common.h @@ -237,6 +237,7 @@ static INLINE void D3D11SetPShaderSamplers( device_context->lpVtbl->PSSetSamplers( device_context, start_slot, num_samplers, samplers); } + static INLINE void D3D11SetVShader( D3D11DeviceContext device_context, D3D11VertexShader vertex_shader, diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 214436e096..98e4791fd8 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -16,16 +16,197 @@ #define CINTERFACE #include + #include +#include #include "../../driver.h" #include "../../verbosity.h" #include "../../configuration.h" #include "../video_driver.h" +#include "../font_driver.h" #include "../common/win32_common.h" #include "../common/d3d10_common.h" #include "../common/dxgi_common.h" #include "../common/d3dcompiler_common.h" +#ifdef HAVE_MENU +#include "../../menu/menu_driver.h" +#endif + +#ifdef HAVE_OVERLAY +static void d3d10_free_overlays(d3d10_video_t* d3d10) +{ + unsigned i; + for (i = 0; i < d3d10->overlays.count; i++) + d3d10_release_texture(&d3d10->overlays.textures[i]); + + Release(d3d10->overlays.vbo); +} + +static void +d3d10_overlay_vertex_geom(void* data, unsigned index, float x, float y, float w, float h) +{ + D3D10_MAPPED_SUBRESOURCE mapped_vbo; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (!d3d10) + return; + + D3D10MapBuffer(d3d10->overlays.vbo, + D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); + { + d3d10_sprite_t* sprites = (d3d10_sprite_t*)mapped_vbo.pData; + sprites[index].pos.x = x; + sprites[index].pos.y = y; + sprites[index].pos.w = w; + sprites[index].pos.h = h; + } + D3D10UnmapBuffer(d3d10->overlays.vbo); +} + +static void d3d10_overlay_tex_geom(void* data, unsigned index, float u, float v, float w, float h) +{ + D3D10_MAPPED_SUBRESOURCE mapped_vbo; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (!d3d10) + return; + + D3D10MapBuffer( + d3d10->overlays.vbo, + D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); + { + d3d10_sprite_t* sprites = (d3d10_sprite_t*)mapped_vbo.pData; + sprites[index].coords.u = u; + sprites[index].coords.v = v; + sprites[index].coords.w = w; + sprites[index].coords.h = h; + } + D3D10UnmapBuffer(d3d10->overlays.vbo); +} + +static void d3d10_overlay_set_alpha(void* data, unsigned index, float mod) +{ + D3D10_MAPPED_SUBRESOURCE mapped_vbo; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (!d3d10) + return; + + D3D10MapBuffer( + d3d10->overlays.vbo, + D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); + + { + d3d10_sprite_t* sprites = (d3d10_sprite_t*)mapped_vbo.pData; + sprites[index].colors[0] = DXGI_COLOR_RGBA(0xFF, 0xFF, 0xFF, mod * 0xFF); + sprites[index].colors[1] = sprites[index].colors[0]; + sprites[index].colors[2] = sprites[index].colors[0]; + sprites[index].colors[3] = sprites[index].colors[0]; + } + D3D10UnmapBuffer(d3d10->overlays.vbo); +} + +static bool d3d10_overlay_load(void* data, const void* image_data, unsigned num_images) +{ + D3D10_BUFFER_DESC desc; + D3D10_MAPPED_SUBRESOURCE mapped_vbo; + int i; + d3d10_sprite_t* sprites; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + const struct texture_image* images = (const struct texture_image*)image_data; + + if (!d3d10) + return false; + + d3d10_free_overlays(d3d10); + d3d10->overlays.count = num_images; + d3d10->overlays.textures = (d3d10_texture_t*)calloc( + num_images, sizeof(d3d10_texture_t)); + + desc.ByteWidth = sizeof(d3d10_sprite_t) * num_images; + desc.Usage = D3D10_USAGE_DYNAMIC; + desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; + desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; +#if 0 + desc.StructureByteStride = 0; +#endif + D3D10CreateBuffer(d3d10->device, &desc, NULL, &d3d10->overlays.vbo); + + D3D10MapBuffer(d3d10->overlays.vbo, + D3D10_MAP_WRITE_DISCARD, 0, (void**)&mapped_vbo); + sprites = (d3d10_sprite_t*)mapped_vbo.pData; + + for (i = 0; i < num_images; i++) + { + + d3d10->overlays.textures[i].desc.Width = images[i].width; + d3d10->overlays.textures[i].desc.Height = images[i].height; + d3d10->overlays.textures[i].desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + + d3d10_init_texture(d3d10->device, &d3d10->overlays.textures[i]); + + d3d10_update_texture( + d3d10->device, + images[i].width, + images[i].height, 0, DXGI_FORMAT_B8G8R8A8_UNORM, + images[i].pixels, &d3d10->overlays.textures[i]); + + sprites[i].pos.x = 0.0f; + sprites[i].pos.y = 0.0f; + sprites[i].pos.w = 1.0f; + sprites[i].pos.h = 1.0f; + + sprites[i].coords.u = 0.0f; + sprites[i].coords.v = 0.0f; + sprites[i].coords.w = 1.0f; + sprites[i].coords.h = 1.0f; + + sprites[i].params.scaling = 1; + sprites[i].params.rotation = 0; + + sprites[i].colors[0] = 0xFFFFFFFF; + sprites[i].colors[1] = sprites[i].colors[0]; + sprites[i].colors[2] = sprites[i].colors[0]; + sprites[i].colors[3] = sprites[i].colors[0]; + } + D3D10UnmapBuffer(d3d10->overlays.vbo); + + return true; +} + +static void d3d10_overlay_enable(void* data, bool state) +{ + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (!d3d10) + return; + + d3d10->overlays.enabled = state; + win32_show_cursor(state); +} + +static void d3d10_overlay_full_screen(void* data, bool enable) +{ + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (!d3d10) + return; + + d3d10->overlays.fullscreen = enable; +} + +static void d3d10_get_overlay_interface(void* data, const video_overlay_interface_t** iface) +{ + static const video_overlay_interface_t overlay_interface = { + d3d10_overlay_enable, d3d10_overlay_load, d3d10_overlay_tex_geom, + d3d10_overlay_vertex_geom, d3d10_overlay_full_screen, d3d10_overlay_set_alpha, + }; + + *iface = &overlay_interface; +} +#endif static void d3d10_set_filtering(void* data, unsigned index, bool smooth) { @@ -73,12 +254,322 @@ static void d3d10_update_viewport(void* data, bool force_full) d3d10->resize_viewport = false; } +static void d3d10_free_shader_preset(d3d10_video_t* d3d10) +{ + unsigned i; + if (!d3d10->shader_preset) + return; + + for (i = 0; i < d3d10->shader_preset->passes; i++) + { + unsigned j; + + free(d3d10->shader_preset->pass[i].source.string.vertex); + free(d3d10->shader_preset->pass[i].source.string.fragment); + free(d3d10->pass[i].semantics.textures); + d3d10_release_shader(&d3d10->pass[i].shader); + d3d10_release_texture(&d3d10->pass[i].rt); + d3d10_release_texture(&d3d10->pass[i].feedback); + + for (j = 0; j < SLANG_CBUFFER_MAX; j++) + { + free(d3d10->pass[i].semantics.cbuffers[j].uniforms); + Release(d3d10->pass[i].buffers[j]); + } + } + + memset(d3d10->pass, 0, sizeof(d3d10->pass)); + + /* only free the history textures here */ + for (i = 1; i <= d3d10->shader_preset->history_size; i++) + d3d10_release_texture(&d3d10->frame.texture[i]); + + memset( + &d3d10->frame.texture[1], 0, + sizeof(d3d10->frame.texture[1]) * d3d10->shader_preset->history_size); + + for (i = 0; i < d3d10->shader_preset->luts; i++) + d3d10_release_texture(&d3d10->luts[i]); + + memset(d3d10->luts, 0, sizeof(d3d10->luts)); + + free(d3d10->shader_preset); + d3d10->shader_preset = NULL; + d3d10->init_history = false; + d3d10->resize_render_targets = false; +} + + +static void d3d10_gfx_free(void* data) +{ + unsigned i; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (!d3d10) + return; + +#ifdef HAVE_OVERLAY + d3d10_free_overlays(d3d10); +#endif + + d3d10_free_shader_preset(d3d10); + + d3d10_release_texture(&d3d10->frame.texture[0]); + Release(d3d10->frame.ubo); + Release(d3d10->frame.vbo); + + d3d10_release_texture(&d3d10->menu.texture); + Release(d3d10->menu.vbo); + + d3d10_release_shader(&d3d10->sprites.shader); + d3d10_release_shader(&d3d10->sprites.shader_font); + Release(d3d10->sprites.vbo); + + for (i = 0; i < GFX_MAX_SHADERS; i++) + d3d10_release_shader(&d3d10->shaders[i]); + + Release(d3d10->menu_pipeline_vbo); + Release(d3d10->blend_pipeline); + + Release(d3d10->ubo); + + Release(d3d10->blend_enable); + Release(d3d10->blend_disable); + + for (i = 0; i < RARCH_WRAP_MAX; i++) + { + Release(d3d10->samplers[RARCH_FILTER_LINEAR][i]); + Release(d3d10->samplers[RARCH_FILTER_NEAREST][i]); + } + + Release(d3d10->state); + Release(d3d10->renderTargetView); + Release(d3d10->swapChain); + + font_driver_free_osd(); + +#if 0 + if (video_driver_is_video_cache_context()) + { + cached_device_d3d10 = d3d10->device; + cached_context = d3d10->context; + } + else +#endif + { + Release(d3d10->device); + } + + win32_monitor_from_window(); + win32_destroy_window(); + free(d3d10); +} + +static bool d3d10_gfx_set_shader(void* data, + enum rarch_shader_type type, const char* path) +{ +#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) + unsigned i; + d3d10_texture_t* source; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (!d3d10) + return false; + + D3D10Flush(d3d10->device); + d3d10_free_shader_preset(d3d10); + + if (!path) + return true; + + if (type != RARCH_SHADER_SLANG) + { + RARCH_WARN("Only .slang or .slangp shaders are supported. Falling back to stock.\n"); + return false; + } + + config_file_t* conf = config_file_new(path); + + if (!conf) + return false; + + d3d10->shader_preset = (struct video_shader*)calloc(1, sizeof(*d3d10->shader_preset)); + + if (!video_shader_read_conf_cgp(conf, d3d10->shader_preset)) + goto error; + + video_shader_resolve_relative(d3d10->shader_preset, path); + + source = &d3d10->frame.texture[0]; + for (i = 0; i < d3d10->shader_preset->passes; source = &d3d10->pass[i++].rt) + { + unsigned j; + /* clang-format off */ + semantics_map_t semantics_map = { + { + /* Original */ + { &d3d10->frame.texture[0].view, 0, + &d3d10->frame.texture[0].size_data, 0}, + + /* Source */ + { &source->view, 0, + &source->size_data, 0}, + + /* OriginalHistory */ + { &d3d10->frame.texture[0].view, sizeof(*d3d10->frame.texture), + &d3d10->frame.texture[0].size_data, sizeof(*d3d10->frame.texture)}, + + /* PassOutput */ + { &d3d10->pass[0].rt.view, sizeof(*d3d10->pass), + &d3d10->pass[0].rt.size_data, sizeof(*d3d10->pass)}, + + /* PassFeedback */ + { &d3d10->pass[0].feedback.view, sizeof(*d3d10->pass), + &d3d10->pass[0].feedback.size_data, sizeof(*d3d10->pass)}, + + /* User */ + { &d3d10->luts[0].view, sizeof(*d3d10->luts), + &d3d10->luts[0].size_data, sizeof(*d3d10->luts)}, + }, + { + &d3d10->mvp, /* MVP */ + &d3d10->pass[i].rt.size_data, /* OutputSize */ + &d3d10->frame.output_size, /* FinalViewportSize */ + &d3d10->pass[i].frame_count, /* FrameCount */ + } + }; + /* clang-format on */ + + if (!slang_process( + d3d10->shader_preset, i, RARCH_SHADER_HLSL, 50, &semantics_map, + &d3d10->pass[i].semantics)) + goto error; + + { + static const D3D10_INPUT_ELEMENT_DESC desc[] = { + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, position), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, texcoord), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; +#ifdef DEBUG + bool save_hlsl = true; +#else + bool save_hlsl = false; +#endif + static const char vs_ext[] = ".vs.hlsl"; + static const char ps_ext[] = ".ps.hlsl"; + char vs_path[PATH_MAX_LENGTH]; + char ps_path[PATH_MAX_LENGTH]; + const char* slang_path = d3d10->shader_preset->pass[i].source.path; + const char* vs_src = d3d10->shader_preset->pass[i].source.string.vertex; + const char* ps_src = d3d10->shader_preset->pass[i].source.string.fragment; + int base_len = strlen(slang_path) - strlen(".slang"); + + if (base_len <= 0) + base_len = strlen(slang_path); + + strncpy(vs_path, slang_path, base_len); + strncpy(ps_path, slang_path, base_len); + strncpy(vs_path + base_len, vs_ext, sizeof(vs_ext)); + strncpy(ps_path + base_len, ps_ext, sizeof(ps_ext)); + + if (!d3d10_init_shader( + d3d10->device, vs_src, 0, vs_path, "main", NULL, NULL, desc, countof(desc), + &d3d10->pass[i].shader)) + save_hlsl = true; + + if (!d3d10_init_shader( + d3d10->device, ps_src, 0, ps_path, NULL, "main", NULL, NULL, 0, + &d3d10->pass[i].shader)) + save_hlsl = true; + + if (save_hlsl) + { + FILE* fp = fopen(vs_path, "w"); + fwrite(vs_src, 1, strlen(vs_src), fp); + fclose(fp); + + fp = fopen(ps_path, "w"); + fwrite(ps_src, 1, strlen(ps_src), fp); + fclose(fp); + } + + free(d3d10->shader_preset->pass[i].source.string.vertex); + free(d3d10->shader_preset->pass[i].source.string.fragment); + + d3d10->shader_preset->pass[i].source.string.vertex = NULL; + d3d10->shader_preset->pass[i].source.string.fragment = NULL; + + if (!d3d10->pass[i].shader.vs || !d3d10->pass[i].shader.ps) + goto error; + } + + for (j = 0; j < SLANG_CBUFFER_MAX; j++) + { + D3D10_BUFFER_DESC desc; + desc.ByteWidth = d3d10->pass[i].semantics.cbuffers[j].size; + desc.Usage = D3D10_USAGE_DYNAMIC; + desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER; + desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; +#if 0 + desc.StructureByteStride = 0; +#endif + + if (!desc.ByteWidth) + continue; + + D3D10CreateBuffer(d3d10->device, &desc, NULL, &d3d10->pass[i].buffers[j]); + } + } + + for (i = 0; i < d3d10->shader_preset->luts; i++) + { + struct texture_image image = { 0 }; + image.supports_rgba = true; + + if (!image_texture_load(&image, d3d10->shader_preset->lut[i].path)) + goto error; + + d3d10->luts[i].desc.Width = image.width; + d3d10->luts[i].desc.Height = image.height; + d3d10->luts[i].desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + + if (d3d10->shader_preset->lut[i].mipmap) + d3d10->luts[i].desc.MiscFlags = D3D10_RESOURCE_MISC_GENERATE_MIPS; + + d3d10_init_texture(d3d10->device, &d3d10->luts[i]); + + d3d10_update_texture( + d3d10->device, + image.width, image.height, 0, DXGI_FORMAT_R8G8B8A8_UNORM, image.pixels, + &d3d10->luts[i]); + + image_texture_free(&image); + } + + video_shader_resolve_current_parameters(conf, d3d10->shader_preset); + config_file_free(conf); + + d3d10->resize_render_targets = true; + d3d10->init_history = true; + + return true; + +error: + d3d10_free_shader_preset(d3d10); +#endif + + return false; +} + static void* d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** input_data) { - WNDCLASSEX wndclass = { 0 }; MONITORINFOEX current_mon; HMONITOR hm_to_use; + WNDCLASSEX wndclass = { 0 }; settings_t* settings = config_get_ptr(); d3d10_video_t* d3d10 = (d3d10_video_t*)calloc(1, sizeof(*d3d10)); @@ -136,9 +627,10 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i flags |= D3D10_CREATE_DEVICE_DEBUG; #endif - D3D10CreateDeviceAndSwapChain( + if (FAILED(D3D10CreateDeviceAndSwapChain( NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, flags, D3D10_SDK_VERSION, &desc, - (IDXGISwapChain**)&d3d10->swapChain, &d3d10->device); + (IDXGISwapChain**)&d3d10->swapChain, &d3d10->device))) + goto error; } { @@ -153,21 +645,28 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i d3d10->viewport.Width = video->width; d3d10->viewport.Height = video->height; d3d10->resize_viewport = true; - d3d10->keep_aspect = video->force_aspect; - d3d10->vsync = video->vsync; - d3d10->format = video->rgb32 ? DXGI_FORMAT_B8G8R8X8_UNORM : DXGI_FORMAT_B5G6R5_UNORM; + d3d10->keep_aspect = video->force_aspect; + d3d10->vsync = video->vsync; + d3d10->format = video->rgb32 ? + DXGI_FORMAT_B8G8R8X8_UNORM : DXGI_FORMAT_B5G6R5_UNORM; - d3d10->frame.texture.desc.Format = - d3d10_get_closest_match_texture2D(d3d10->device, d3d10->format); - d3d10->frame.texture.desc.Usage = D3D10_USAGE_DEFAULT; + d3d10->frame.texture[0].desc.Format = d3d10_get_closest_match_texture2D(d3d10->device, d3d10->format); + d3d10->frame.texture[0].desc.Usage = D3D10_USAGE_DEFAULT; + d3d10->frame.texture[0].desc.Width = 4; + d3d10->frame.texture[0].desc.Height = 4; + + d3d10_init_texture(d3d10->device, &d3d10->frame.texture[0]); d3d10->menu.texture.desc.Usage = D3D10_USAGE_DEFAULT; matrix_4x4_ortho(d3d10->mvp_no_rot, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); + d3d10->ubo_values.OutputSize.width = d3d10->viewport.Width; + d3d10->ubo_values.OutputSize.height = d3d10->viewport.Height; + { - D3D10_BUFFER_DESC desc; D3D10_SUBRESOURCE_DATA ubo_data; + D3D10_BUFFER_DESC desc; desc.ByteWidth = sizeof(math_matrix_4x4); desc.Usage = D3D10_USAGE_DYNAMIC; @@ -182,9 +681,11 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i D3D10CreateBuffer(d3d10->device, &desc, &ubo_data, &d3d10->ubo); D3D10CreateBuffer(d3d10->device, &desc, NULL, &d3d10->frame.ubo); } + d3d10_gfx_set_rotation(d3d10, 0); { + unsigned i; unsigned k; D3D10_SAMPLER_DESC desc; @@ -201,86 +702,189 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i for (k = 0; k < 4; k++) desc.BorderColor[k] = 0.0f; - D3D10CreateSamplerState(d3d10->device, &desc, &d3d10->sampler_nearest); + /* Initialize samplers */ + for (i = 0; i < RARCH_WRAP_MAX; i++) + { + switch (i) + { + case RARCH_WRAP_BORDER: + desc.AddressU = D3D10_TEXTURE_ADDRESS_BORDER; + break; - desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR; - D3D10CreateSamplerState(d3d10->device, &desc, &d3d10->sampler_linear); + case RARCH_WRAP_EDGE: + desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP; + break; + + case RARCH_WRAP_REPEAT: + desc.AddressU = D3D10_TEXTURE_ADDRESS_WRAP; + break; + + case RARCH_WRAP_MIRRORED_REPEAT: + desc.AddressU = D3D10_TEXTURE_ADDRESS_MIRROR; + break; + } + desc.AddressV = desc.AddressU; + desc.AddressW = desc.AddressU; + + desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR; + D3D10CreateSamplerState(d3d10->device, &desc, + &d3d10->samplers[RARCH_FILTER_LINEAR][i]); + + desc.Filter = D3D10_FILTER_MIN_MAG_MIP_POINT; + D3D10CreateSamplerState(d3d10->device, &desc, + &d3d10->samplers[RARCH_FILTER_NEAREST][i]); + } } d3d10_set_filtering(d3d10, 0, video->smooth); { + D3D10_BUFFER_DESC desc; d3d10_vertex_t vertices[] = { { { 0.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, { { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, { { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, { { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, }; + D3D10_SUBRESOURCE_DATA + vertexData = { vertices }; - { - D3D10_SUBRESOURCE_DATA vertexData; - D3D10_BUFFER_DESC desc; + desc.ByteWidth = sizeof(vertices); + desc.Usage = D3D10_USAGE_IMMUTABLE; + desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; - desc.ByteWidth = sizeof(vertices); - desc.Usage = D3D10_USAGE_DYNAMIC; - desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; - desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; - desc.MiscFlags = 0; + D3D10CreateBuffer(d3d10->device, &desc, &vertexData, &d3d10->frame.vbo); + desc.Usage = D3D10_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; + D3D10CreateBuffer(d3d10->device, &desc, &vertexData, &d3d10->menu.vbo); - vertexData.pSysMem = vertices; - vertexData.SysMemPitch = 0; - vertexData.SysMemSlicePitch = 0; - - D3D10CreateBuffer(d3d10->device, &desc, &vertexData, &d3d10->frame.vbo); - desc.Usage = D3D10_USAGE_IMMUTABLE; - desc.CPUAccessFlags = 0; - D3D10CreateBuffer(d3d10->device, &desc, &vertexData, &d3d10->menu.vbo); - } - D3D10SetPrimitiveTopology(d3d10->device, D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + d3d10->sprites.capacity = 4096; + desc.ByteWidth = sizeof(d3d10_sprite_t) * d3d10->sprites.capacity; + D3D10CreateBuffer(d3d10->device, &desc, NULL, &d3d10->sprites.vbo); } { - D3DBlob vs_code; - D3DBlob ps_code; - - static const char stock[] = -#include "d3d_shaders/opaque_sm5.hlsl.h" - ; - D3D10_INPUT_ELEMENT_DESC desc[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, position), - D3D10_INPUT_PER_VERTEX_DATA, 0 }, + D3D10_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, texcoord), - D3D10_INPUT_PER_VERTEX_DATA, 0 }, + D3D10_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(d3d10_vertex_t, color), - D3D10_INPUT_PER_VERTEX_DATA, 0 }, + D3D10_INPUT_PER_VERTEX_DATA, 0 }, }; - d3d_compile(stock, sizeof(stock), NULL, "VSMain", "vs_4_0", &vs_code); - d3d_compile(stock, sizeof(stock), NULL, "PSMain", "ps_4_0", &ps_code); + static const char shader[] = +#include "d3d_shaders/opaque_sm5.hlsl.h" + ; - D3D10CreateVertexShader( - d3d10->device, D3DGetBufferPointer(vs_code), D3DGetBufferSize(vs_code), &d3d10->vs); - D3D10CreatePixelShader( - d3d10->device, D3DGetBufferPointer(ps_code), D3DGetBufferSize(ps_code), &d3d10->ps); - D3D10CreateInputLayout( - d3d10->device, desc, countof(desc), D3DGetBufferPointer(vs_code), - D3DGetBufferSize(vs_code), &d3d10->layout); - - Release(vs_code); - Release(ps_code); + if (!d3d10_init_shader( + d3d10->device, shader, sizeof(shader), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d10->shaders[VIDEO_SHADER_STOCK_BLEND])) + goto error; } - D3D10SetInputLayout(d3d10->device, d3d10->layout); - D3D10SetVShader(d3d10->device, d3d10->vs); - D3D10SetPShader(d3d10->device, d3d10->ps); + { + D3D10_INPUT_ELEMENT_DESC desc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(d3d10_sprite_t, pos), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(d3d10_sprite_t, coords), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(d3d10_sprite_t, colors[0]), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(d3d10_sprite_t, colors[1]), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 2, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(d3d10_sprite_t, colors[2]), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 3, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(d3d10_sprite_t, colors[3]), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "PARAMS", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_sprite_t, params), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; + + static const char shader[] = +#include "d3d_shaders/sprite_sm4.hlsl.h" + ; + + if (!d3d10_init_shader( + d3d10->device, shader, sizeof(shader), NULL, "VSMain", "PSMain", "GSMain", desc, + countof(desc), &d3d10->sprites.shader)) + goto error; + if (!d3d10_init_shader( + d3d10->device, shader, sizeof(shader), NULL, "VSMain", "PSMainA8", "GSMain", desc, + countof(desc), &d3d10->sprites.shader_font)) + goto error; + } + + { + D3D10_INPUT_ELEMENT_DESC desc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; + + static const char ribbon[] = +#include "d3d_shaders/ribbon_sm4.hlsl.h" + ; + static const char ribbon_simple[] = +#include "d3d_shaders/ribbon_simple_sm4.hlsl.h" + ; + + if (!d3d10_init_shader( + d3d10->device, ribbon, sizeof(ribbon), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU])) + goto error; + + if (!d3d10_init_shader( + d3d10->device, ribbon_simple, sizeof(ribbon_simple), NULL, "VSMain", "PSMain", NULL, + desc, countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_2])) + goto error; + } + + { + D3D10_INPUT_ELEMENT_DESC desc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, position), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, texcoord), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; + + static const char simple_snow[] = +#include "d3d_shaders/simple_snow_sm4.hlsl.h" + ; + static const char snow[] = +#include "d3d_shaders/snow_sm4.hlsl.h" + ; + static const char bokeh[] = +#include "d3d_shaders/bokeh_sm4.hlsl.h" + ; + static const char snowflake[] = +#include "d3d_shaders/snowflake_sm4.hlsl.h" + ; + + if (!d3d10_init_shader( + d3d10->device, simple_snow, sizeof(simple_snow), NULL, "VSMain", "PSMain", NULL, + desc, countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_3])) + goto error; + if (!d3d10_init_shader( + d3d10->device, snow, sizeof(snow), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_4])) + goto error; + + if (!d3d10_init_shader( + d3d10->device, bokeh, sizeof(bokeh), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_5])) + goto error; + + if (!d3d10_init_shader( + d3d10->device, snowflake, sizeof(snowflake), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_6])) + goto error; + } { unsigned k; D3D10_BLEND_DESC blend_desc; - - for (k = 0; k < 8; k++) { blend_desc.BlendEnable[k] = TRUE; @@ -300,13 +904,171 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i D3D10CreateBlendState(d3d10->device, &blend_desc, &d3d10->blend_disable); } + { + D3D10_RASTERIZER_DESC desc = { (D3D10_FILL_MODE)0 }; + + desc.FillMode = D3D10_FILL_SOLID; + desc.CullMode = D3D10_CULL_NONE; + + D3D10CreateRasterizerState(d3d10->device, &desc, &d3d10->state); + } + + D3D10SetState(d3d10->device, d3d10->state); + + font_driver_init_osd(d3d10, false, video->is_threaded, FONT_DRIVER_RENDER_D3D10_API); + + if (settings->bools.video_shader_enable) + { + const char* ext = path_get_extension(settings->paths.path_shader); + + if (ext && !strncmp(ext, "slang", 5)) + d3d10_gfx_set_shader(d3d10, RARCH_SHADER_SLANG, settings->paths.path_shader); + } + +#if 0 + if (video_driver_get_hw_context()->context_type == RETRO_HW_CONTEXT_DIRECT3D && + video_driver_get_hw_context()->version_major == 11) + { + d3d10->hw.enable = true; + d3d10->hw.iface.interface_type = RETRO_HW_RENDER_INTERFACE_D3D11; + d3d10->hw.iface.interface_version = RETRO_HW_RENDER_INTERFACE_D3D11_VERSION; + d3d10->hw.iface.handle = d3d10; + d3d10->hw.iface.device = d3d10->device; + d3d10->hw.iface.context = d3d10->context; + d3d10->hw.iface.featureLevel = d3d10->supportedFeatureLevel; + d3d10->hw.iface.D3DCompile = D3DCompile; + } +#endif + return d3d10; error: - free(d3d10); + d3d10_gfx_free(d3d10); return NULL; } +static void d3d10_init_history(d3d10_video_t* d3d10, unsigned width, unsigned height) +{ + unsigned i; + + /* todo: should we init history to max_width/max_height instead ? + * to prevent out of memory errors happening several frames later + * and to reduce memory fragmentation */ + + assert(d3d10->shader_preset); + for (i = 0; i < d3d10->shader_preset->history_size + 1; i++) + { + d3d10->frame.texture[i].desc.Width = width; + d3d10->frame.texture[i].desc.Height = height; + d3d10->frame.texture[i].desc.Format = d3d10->frame.texture[0].desc.Format; + d3d10->frame.texture[i].desc.Usage = d3d10->frame.texture[0].desc.Usage; + d3d10_init_texture(d3d10->device, &d3d10->frame.texture[i]); + /* todo: clear texture ? */ + } + d3d10->init_history = false; +} + +static void d3d10_init_render_targets(d3d10_video_t* d3d10, + unsigned width, unsigned height) +{ + unsigned i; + + assert(d3d10->shader_preset); + + for (i = 0; i < d3d10->shader_preset->passes; i++) + { + struct video_shader_pass* pass = &d3d10->shader_preset->pass[i]; + + if (pass->fbo.valid) + { + + switch (pass->fbo.type_x) + { + case RARCH_SCALE_INPUT: + width *= pass->fbo.scale_x; + break; + + case RARCH_SCALE_VIEWPORT: + width = d3d10->vp.width * pass->fbo.scale_x; + break; + + case RARCH_SCALE_ABSOLUTE: + width = pass->fbo.abs_x; + break; + + default: + break; + } + + if (!width) + width = d3d10->vp.width; + + switch (pass->fbo.type_y) + { + case RARCH_SCALE_INPUT: + height *= pass->fbo.scale_y; + break; + + case RARCH_SCALE_VIEWPORT: + height = d3d10->vp.height * pass->fbo.scale_y; + break; + + case RARCH_SCALE_ABSOLUTE: + height = pass->fbo.abs_y; + break; + + default: + break; + } + + if (!height) + height = d3d10->vp.height; + } + else if (i == (d3d10->shader_preset->passes - 1)) + { + width = d3d10->vp.width; + height = d3d10->vp.height; + } + + RARCH_LOG("[D3D10]: Updating framebuffer size %u x %u.\n", width, height); + + if ((i != (d3d10->shader_preset->passes - 1)) || (width != d3d10->vp.width) || + (height != d3d10->vp.height)) + { + d3d10->pass[i].viewport.Width = width; + d3d10->pass[i].viewport.Height = height; + d3d10->pass[i].viewport.MaxDepth = 1.0; + d3d10->pass[i].rt.desc.Width = width; + d3d10->pass[i].rt.desc.Height = height; + d3d10->pass[i].rt.desc.BindFlags = D3D10_BIND_RENDER_TARGET; + d3d10->pass[i].rt.desc.Format = glslang_format_to_dxgi(d3d10->pass[i].semantics.format); + d3d10_init_texture(d3d10->device, &d3d10->pass[i].rt); + + if (pass->feedback) + { + d3d10->pass[i].feedback.desc = d3d10->pass[i].rt.desc; + d3d10_init_texture(d3d10->device, &d3d10->pass[i].feedback); + /* todo: do we need to clear it to black here ? */ + } + } + else + { + d3d10->pass[i].rt.size_data.x = width; + d3d10->pass[i].rt.size_data.y = height; + d3d10->pass[i].rt.size_data.z = 1.0f / width; + d3d10->pass[i].rt.size_data.w = 1.0f / height; + } + } + + d3d10->resize_render_targets = false; + +#if 0 +error: + d3d10_free_shader_preset(d3d10); + return false; +#endif +} + static bool d3d10_gfx_frame( void* data, const void* frame, @@ -317,9 +1079,10 @@ static bool d3d10_gfx_frame( const char* msg, video_frame_info_t* video_info) { - (void)msg; - - d3d10_video_t* d3d10 = (d3d10_video_t*)data; + unsigned i; + d3d10_texture_t* texture = NULL; + d3d10_video_t * d3d10 = (d3d10_video_t*)data; + D3D10Device context = d3d10->device; if (d3d10->resize_chain) { @@ -337,71 +1100,291 @@ static bool d3d10_gfx_frame( d3d10->viewport.Width = video_info->width; d3d10->viewport.Height = video_info->height; + d3d10->ubo_values.OutputSize.width = d3d10->viewport.Width; + d3d10->ubo_values.OutputSize.height = d3d10->viewport.Height; + d3d10->resize_chain = false; d3d10->resize_viewport = true; + + video_driver_set_size(&video_info->width, &video_info->height); } PERF_START(); - D3D10ClearRenderTargetView(d3d10->device, d3d10->renderTargetView, d3d10->clearcolor); + +#if 0 /* custom viewport doesn't call apply_state_changes, so we can't rely on this for now */ + if (d3d10->resize_viewport) +#endif + d3d10_update_viewport(d3d10, false); + + D3D10SetPrimitiveTopology(context, D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + +#if 0 + if (d3d10->hw.enable) + { + D3D10SetRenderTargets(context, 1, &d3d10->renderTargetView, NULL); + D3D10SetState(context, d3d10->state); + } +#endif if (frame && width && height) { - D3D10_BOX frame_box = { 0, 0, 0, width, height, 1 }; - - if (d3d10->frame.texture.desc.Width != width || d3d10->frame.texture.desc.Height != height) + if (d3d10->shader_preset) { - d3d10->frame.texture.desc.Width = width; - d3d10->frame.texture.desc.Height = height; - d3d10_init_texture(d3d10->device, &d3d10->frame.texture); + if (d3d10->frame.texture[0].desc.Width != width || + d3d10->frame.texture[0].desc.Height != height) + d3d10->resize_render_targets = true; + + if (d3d10->resize_render_targets) + { + unsigned i; + + /* release all render targets first to avoid memory fragmentation */ + for (i = 0; i < d3d10->shader_preset->passes; i++) + { + d3d10_release_texture(&d3d10->pass[i].rt); + d3d10_release_texture(&d3d10->pass[i].feedback); + memset(&d3d10->pass[i].rt, 0, sizeof(d3d10->pass[i].rt)); + memset(&d3d10->pass[i].feedback, 0, sizeof(d3d10->pass[i].feedback)); + } + } + + if (d3d10->shader_preset->history_size) + { + if (d3d10->init_history) + d3d10_init_history(d3d10, width, height); + else + { + int k; + /* todo: what about frame-duping ? + * maybe clone d3d10_texture_t with AddRef */ + d3d10_texture_t tmp = d3d10->frame.texture[d3d10->shader_preset->history_size]; + for (k = d3d10->shader_preset->history_size; k > 0; k--) + d3d10->frame.texture[k] = d3d10->frame.texture[k - 1]; + d3d10->frame.texture[0] = tmp; + } + } } - d3d10_update_texture(width, height, pitch, d3d10->format, frame, &d3d10->frame.texture); - D3D10CopyTexture2DSubresourceRegion( - d3d10->device, d3d10->frame.texture.handle, 0, 0, 0, 0, d3d10->frame.texture.staging, 0, - &frame_box); + /* either no history, or we moved a texture of a different size in the front slot */ + if (d3d10->frame.texture[0].desc.Width != width || + d3d10->frame.texture[0].desc.Height != height) + { + d3d10->frame.texture[0].desc.Width = width; + d3d10->frame.texture[0].desc.Height = height; + d3d10_init_texture(d3d10->device, &d3d10->frame.texture[0]); + } + + if (d3d10->resize_render_targets) + d3d10_init_render_targets(d3d10, width, height); + + if (frame != RETRO_HW_FRAME_BUFFER_VALID) + d3d10_update_texture( + d3d10->device, + width, height, pitch, d3d10->format, frame, &d3d10->frame.texture[0]); } + D3D10SetVertexBuffer(context, 0, d3d10->frame.vbo, sizeof(d3d10_vertex_t), 0); + D3D10SetBlendState(context, d3d10->blend_disable, NULL, D3D10_DEFAULT_SAMPLE_MASK); + + texture = d3d10->frame.texture; + + if (d3d10->shader_preset) { - UINT stride = sizeof(d3d10_vertex_t); - UINT offset = 0; -#if 0 /* custom viewport doesn't call apply_state_changes, so we can't rely on this for now */ - if (d3d10->resize_viewport) -#endif - d3d10_update_viewport(d3d10, false); + unsigned i; - D3D10SetViewports(d3d10->device, 1, &d3d10->frame.viewport); - D3D10SetVertexBuffers(d3d10->device, 0, 1, &d3d10->frame.vbo, &stride, &offset); - D3D10SetVShaderConstantBuffers(d3d10->device, 0, 1, &d3d10->frame.ubo); - D3D10SetPShaderResources(d3d10->device, 0, 1, &d3d10->frame.texture.view); - D3D10SetPShaderSamplers(d3d10->device, 0, 1, &d3d10->frame.sampler); - - D3D10SetBlendState(d3d10->device, d3d10->blend_disable, NULL, D3D10_DEFAULT_SAMPLE_MASK); - D3D10Draw(d3d10->device, 4, 0); - D3D10SetBlendState(d3d10->device, d3d10->blend_enable, NULL, D3D10_DEFAULT_SAMPLE_MASK); - - if (d3d10->menu.enabled && d3d10->menu.texture.handle) + for (i = 0; i < d3d10->shader_preset->passes; i++) { - if (d3d10->menu.texture.dirty) - D3D10CopyTexture2DSubresourceRegion( - d3d10->device, d3d10->menu.texture.handle, 0, 0, 0, 0, - d3d10->menu.texture.staging, 0, NULL); + if (d3d10->shader_preset->pass[i].feedback) + { + d3d10_texture_t tmp = d3d10->pass[i].feedback; + d3d10->pass[i].feedback = d3d10->pass[i].rt; + d3d10->pass[i].rt = tmp; + } + } - if (d3d10->menu.fullscreen) - D3D10SetViewports(d3d10->device, 1, &d3d10->viewport); - D3D10SetVertexBuffers(d3d10->device, 0, 1, &d3d10->menu.vbo, &stride, &offset); - D3D10SetVShaderConstantBuffers(d3d10->device, 0, 1, &d3d10->ubo); - D3D10SetPShaderResources(d3d10->device, 0, 1, &d3d10->menu.texture.view); - D3D10SetPShaderSamplers(d3d10->device, 0, 1, &d3d10->menu.sampler); + for (i = 0; i < d3d10->shader_preset->passes; i++) + { + unsigned j; - D3D10Draw(d3d10->device, 4, 0); + d3d10_set_shader(context, &d3d10->pass[i].shader); + + if (d3d10->shader_preset->pass[i].frame_count_mod) + d3d10->pass[i].frame_count = + frame_count % d3d10->shader_preset->pass[i].frame_count_mod; + else + d3d10->pass[i].frame_count = frame_count; + + for (j = 0; j < SLANG_CBUFFER_MAX; j++) + { + D3D10Buffer buffer = d3d10->pass[i].buffers[j]; + cbuffer_sem_t* buffer_sem = &d3d10->pass[i].semantics.cbuffers[j]; + + if (buffer_sem->stage_mask && buffer_sem->uniforms) + { + D3D10_MAPPED_SUBRESOURCE res; + uniform_sem_t* uniform = buffer_sem->uniforms; + + D3D10MapBuffer(buffer, D3D10_MAP_WRITE_DISCARD, 0, (void**)&res); + while (uniform->size) + { + if (uniform->data) + memcpy((uint8_t*)res.pData + uniform->offset, uniform->data, uniform->size); + uniform++; + } + D3D10UnmapBuffer(buffer); + + if (buffer_sem->stage_mask & SLANG_STAGE_VERTEX_MASK) + D3D10SetVShaderConstantBuffers(context, buffer_sem->binding, 1, &buffer); + + if (buffer_sem->stage_mask & SLANG_STAGE_FRAGMENT_MASK) + D3D10SetPShaderConstantBuffers(context, buffer_sem->binding, 1, &buffer); + } + } + + { + D3D10RenderTargetView null_rt = NULL; + D3D10SetRenderTargets(context, 1, &null_rt, NULL); + } + + { + D3D10ShaderResourceView textures[SLANG_NUM_BINDINGS] = { NULL }; + D3D10SamplerState samplers[SLANG_NUM_BINDINGS] = { NULL }; + + texture_sem_t* texture_sem = d3d10->pass[i].semantics.textures; + while (texture_sem->stage_mask) + { + int binding = texture_sem->binding; + textures[binding] = *(D3D10ShaderResourceView*)texture_sem->texture_data; + samplers[binding] = d3d10->samplers[texture_sem->filter][texture_sem->wrap]; + texture_sem++; + } + +#if 0 + if (d3d10->hw.enable && (i == 0)) + D3D10SetPShaderResources(context, 1, SLANG_NUM_BINDINGS - 1, textures + 1); + else +#endif + D3D10SetPShaderResources(context, 0, SLANG_NUM_BINDINGS, textures); + + D3D10SetPShaderSamplers(context, 0, SLANG_NUM_BINDINGS, samplers); + } + + if (d3d10->pass[i].rt.handle) + { + D3D10SetRenderTargets(context, 1, &d3d10->pass[i].rt.rt_view, NULL); +#if 0 + D3D10ClearRenderTargetView(context, d3d10->pass[i].rt.rt_view, d3d10->clearcolor); +#endif + D3D10SetViewports(context, 1, &d3d10->pass[i].viewport); + + D3D10Draw(context, 4, 0); + texture = &d3d10->pass[i].rt; + } + else + { + texture = NULL; + break; + } + } + D3D10SetRenderTargets(context, 1, &d3d10->renderTargetView, NULL); + } + + if (texture) + { + d3d10_set_shader(context, &d3d10->shaders[VIDEO_SHADER_STOCK_BLEND]); +#if 0 + /* TODO/FIXME */ + if (!d3d10->hw.enable || d3d10->shader_preset) +#endif + D3D10SetPShaderResources(context, 0, 1, &texture->view); + D3D10SetPShaderSamplers( + context, 0, 1, &d3d10->samplers[RARCH_FILTER_UNSPEC][RARCH_WRAP_DEFAULT]); + D3D10SetVShaderConstantBuffers(context, 0, 1, &d3d10->frame.ubo); + } + + D3D10ClearRenderTargetView(context, d3d10->renderTargetView, d3d10->clearcolor); + D3D10SetViewports(context, 1, &d3d10->frame.viewport); + + D3D10Draw(context, 4, 0); + + D3D10SetBlendState(context, d3d10->blend_enable, NULL, D3D10_DEFAULT_SAMPLE_MASK); + + if (d3d10->menu.enabled && d3d10->menu.texture.handle) + { + if (d3d10->menu.fullscreen) + D3D10SetViewports(context, 1, &d3d10->viewport); + + d3d10_set_shader(context, &d3d10->shaders[VIDEO_SHADER_STOCK_BLEND]); + D3D10SetVertexBuffer(context, 0, d3d10->menu.vbo, sizeof(d3d10_vertex_t), 0); + D3D10SetVShaderConstantBuffers(context, 0, 1, &d3d10->ubo); + d3d10_set_texture_and_sampler(context, 0, &d3d10->menu.texture); + D3D10Draw(context, 4, 0); + } + + d3d10_set_shader(context, &d3d10->sprites.shader); + D3D10SetPrimitiveTopology(context, D3D10_PRIMITIVE_TOPOLOGY_POINTLIST); + D3D10SetVShaderConstantBuffer(context, 0, d3d10->ubo); + D3D10SetPShaderConstantBuffer(context, 0, d3d10->ubo); + + d3d10->sprites.enabled = true; + +#ifdef HAVE_MENU + if (d3d10->menu.enabled) + { + D3D10SetViewports(context, 1, &d3d10->viewport); + D3D10SetVertexBuffer(context, 0, d3d10->sprites.vbo, sizeof(d3d10_sprite_t), 0); + menu_driver_frame(video_info); + } + else +#endif + if (video_info->statistics_show) + { + struct font_params* osd_params = (struct font_params*)&video_info->osd_stat_params; + + if (osd_params) + { + D3D10SetViewports(context, 1, &d3d10->viewport); + D3D10SetBlendState(d3d10->device, d3d10->blend_enable, NULL, D3D10_DEFAULT_SAMPLE_MASK); + D3D10SetVertexBuffer(context, 0, d3d10->sprites.vbo, sizeof(d3d10_sprite_t), 0); + font_driver_render_msg( + video_info, NULL, video_info->stat_text, + (const struct font_params*)&video_info->osd_stat_params); } } - DXGIPresent(d3d10->swapChain, !!d3d10->vsync, 0); - PERF_STOP(); +#ifdef HAVE_OVERLAY + if (d3d10->overlays.enabled) + { + if (d3d10->overlays.fullscreen) + D3D10SetViewports(context, 1, &d3d10->viewport); + else + D3D10SetViewports(context, 1, &d3d10->frame.viewport); + + D3D10SetBlendState(d3d10->device, d3d10->blend_enable, NULL, D3D10_DEFAULT_SAMPLE_MASK); + D3D10SetVertexBuffer(context, 0, d3d10->overlays.vbo, sizeof(d3d10_sprite_t), 0); + D3D10SetPShaderSamplers( + context, 0, 1, &d3d10->samplers[RARCH_FILTER_UNSPEC][RARCH_WRAP_DEFAULT]); + + for (i = 0; i < d3d10->overlays.count; i++) + { + D3D10SetPShaderResources(context, 0, 1, &d3d10->overlays.textures[i].view); + D3D10Draw(d3d10->device, 1, i); + } + } +#endif if (msg && *msg) + { + D3D10SetViewports(d3d10->device, 1, &d3d10->viewport); + D3D10SetBlendState(d3d10->device, d3d10->blend_enable, NULL, D3D10_DEFAULT_SAMPLE_MASK); + D3D10SetVertexBuffer(d3d10->device, 0, d3d10->sprites.vbo, sizeof(d3d10_sprite_t), 0); + font_driver_render_msg(video_info, NULL, msg, NULL); dxgi_update_title(video_info); + } + d3d10->sprites.enabled = false; + + PERF_STOP(); + DXGIPresent(d3d10->swapChain, !!d3d10->vsync, 0); return true; } @@ -440,45 +1423,14 @@ static bool d3d10_gfx_has_windowed(void* data) return true; } -static void d3d10_gfx_free(void* data) +static struct video_shader* d3d10_gfx_get_current_shader(void* data) { d3d10_video_t* d3d10 = (d3d10_video_t*)data; - Release(d3d10->frame.ubo); - Release(d3d10->frame.vbo); - Release(d3d10->frame.texture.view); - Release(d3d10->frame.texture.handle); - Release(d3d10->frame.texture.staging); + if (!d3d10) + return NULL; - Release(d3d10->menu.texture.handle); - Release(d3d10->menu.texture.staging); - Release(d3d10->menu.texture.view); - Release(d3d10->menu.vbo); - - Release(d3d10->ubo); - Release(d3d10->blend_enable); - Release(d3d10->blend_disable); - Release(d3d10->sampler_nearest); - Release(d3d10->sampler_linear); - Release(d3d10->ps); - Release(d3d10->vs); - Release(d3d10->layout); - Release(d3d10->renderTargetView); - Release(d3d10->swapChain); - Release(d3d10->device); - - win32_monitor_from_window(); - win32_destroy_window(); - free(d3d10); -} - -static bool d3d10_gfx_set_shader(void* data, enum rarch_shader_type type, const char* path) -{ - (void)data; - (void)type; - (void)path; - - return false; + return d3d10->shader_preset; } static void d3d10_gfx_viewport_info(void* data, struct video_viewport* vp) @@ -509,17 +1461,19 @@ static void d3d10_set_menu_texture_frame( if ( d3d10->menu.texture.desc.Width != width || d3d10->menu.texture.desc.Height != height) { - d3d10->menu.texture.desc.Format = d3d10_get_closest_match_texture2D( - d3d10->device, format); + d3d10->menu.texture.desc.Format = format; d3d10->menu.texture.desc.Width = width; d3d10->menu.texture.desc.Height = height; d3d10_init_texture(d3d10->device, &d3d10->menu.texture); } - d3d10_update_texture(width, height, pitch, format, + d3d10_update_texture(d3d10->device, + width, height, pitch, format, frame, &d3d10->menu.texture); - d3d10->menu.sampler = settings->bools.menu_linear_filter - ? d3d10->sampler_linear : d3d10->sampler_nearest; + d3d10->menu.texture.sampler = d3d10->samplers + [settings->bools.menu_linear_filter + ? RARCH_FILTER_LINEAR + : RARCH_FILTER_NEAREST][RARCH_WRAP_DEFAULT]; } static void d3d10_set_menu_texture_enable(void* data, bool state, bool full_screen) @@ -545,17 +1499,98 @@ static void d3d10_gfx_apply_state_changes(void* data) { d3d10_video_t* d3d10 = (d3d10_video_t*)data; -#if 0 if (d3d10) d3d10->resize_viewport = true; -#endif } +static void d3d10_gfx_set_osd_msg( + void* data, video_frame_info_t* video_info, const char* msg, const void* params, void* font) +{ + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (d3d10) + { + if (d3d10->sprites.enabled) + font_driver_render_msg(video_info, font, msg, (const struct font_params*)params); + else + printf("OSD msg: %s\n", msg); + } +} + +static void d3d10_gfx_show_mouse(void* data, bool state) { win32_show_cursor(state); } + +static uintptr_t d3d10_gfx_load_texture( + void* video_data, void* data, bool threaded, enum texture_filter_type filter_type) +{ + d3d10_texture_t* texture = NULL; + d3d10_video_t* d3d10 = (d3d10_video_t*)video_data; + struct texture_image* image = (struct texture_image*)data; + + if (!d3d10) + return 0; + + texture = (d3d10_texture_t*)calloc(1, sizeof(*texture)); + + if (!texture) + return 0; + + switch (filter_type) + { + case TEXTURE_FILTER_MIPMAP_LINEAR: + texture->desc.MiscFlags = D3D10_RESOURCE_MISC_GENERATE_MIPS; + /* fallthrough */ + case TEXTURE_FILTER_LINEAR: + texture->sampler = d3d10->samplers[RARCH_FILTER_LINEAR][RARCH_WRAP_EDGE]; + break; + case TEXTURE_FILTER_MIPMAP_NEAREST: + texture->desc.MiscFlags = D3D10_RESOURCE_MISC_GENERATE_MIPS; + /* fallthrough */ + case TEXTURE_FILTER_NEAREST: + texture->sampler = d3d10->samplers[RARCH_FILTER_NEAREST][RARCH_WRAP_EDGE]; + break; + } + + texture->desc.Width = image->width; + texture->desc.Height = image->height; + texture->desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + + d3d10_init_texture(d3d10->device, texture); + + d3d10_update_texture( + d3d10->device, + image->width, image->height, 0, DXGI_FORMAT_B8G8R8A8_UNORM, image->pixels, + texture); + + return (uintptr_t)texture; +} +static void d3d10_gfx_unload_texture(void* data, uintptr_t handle) +{ + d3d10_texture_t* texture = (d3d10_texture_t*)handle; + + if (!texture) + return; + + Release(texture->view); + Release(texture->staging); + Release(texture->handle); + free(texture); +} + +#if 0 +static bool +d3d10_get_hw_render_interface(void* data, const struct retro_hw_render_interface** iface) +{ + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + *iface = (const struct retro_hw_render_interface*)&d3d10->hw.iface; + return d3d10->hw.enable; +} +#endif + static const video_poke_interface_t d3d10_poke_interface = { NULL, /* set_coords */ NULL, /* set_mvp */ - NULL, /* load_texture */ - NULL, /* unload_texture */ + d3d10_gfx_load_texture, + d3d10_gfx_unload_texture, NULL, /* set_video_mode */ win32_get_refresh_rate, d3d10_set_filtering, @@ -568,12 +1603,16 @@ static const video_poke_interface_t d3d10_poke_interface = { d3d10_gfx_apply_state_changes, d3d10_set_menu_texture_frame, d3d10_set_menu_texture_enable, - NULL, /* set_osd_msg */ - NULL, /* show_mouse */ + d3d10_gfx_set_osd_msg, + d3d10_gfx_show_mouse, NULL, /* grab_mouse_toggle */ - NULL, /* get_current_shader */ + d3d10_gfx_get_current_shader, NULL, /* get_current_software_framebuffer */ +#if 0 + d3d11_get_hw_render_interface, +#else NULL, /* get_hw_render_interface */ +#endif }; static void d3d10_gfx_get_poke_interface(void* data, const video_poke_interface_t** iface) @@ -599,7 +1638,7 @@ video_driver_t video_d3d10 = { NULL, /* read_frame_raw */ #ifdef HAVE_OVERLAY - NULL, /* overlay_interface */ + d3d10_get_overlay_interface, #endif d3d10_gfx_get_poke_interface, }; diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index fe29c3406b..839b8c7608 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -996,6 +996,7 @@ static void d3d11_init_history(d3d11_video_t* d3d11, unsigned width, unsigned he } d3d11->init_history = false; } + static void d3d11_init_render_targets(d3d11_video_t* d3d11, unsigned width, unsigned height) { unsigned i; diff --git a/gfx/drivers_font/d3d10_font.c b/gfx/drivers_font/d3d10_font.c new file mode 100644 index 0000000000..43a0c309c3 --- /dev/null +++ b/gfx/drivers_font/d3d10_font.c @@ -0,0 +1,379 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2018 - Daniel De Matteis + * Copyright (C) 2014-2018 - Ali Bouhlel + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#define CINTERFACE + +#include +#include +#include +#include +#include + +#include "../font_driver.h" +#include "../video_driver.h" +#include "../common/d3d10_common.h" + +#include "../../verbosity.h" + +typedef struct +{ + d3d10_texture_t texture; + const font_renderer_driver_t* font_driver; + void* font_data; + struct font_atlas* atlas; +} d3d10_font_t; + +static void* +d3d10_font_init_font(void* data, const char* font_path, float font_size, bool is_threaded) +{ + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + d3d10_font_t* font = (d3d10_font_t*)calloc(1, sizeof(*font)); + + if (!font) + return NULL; + + if (!font_renderer_create_default( + (const void**)&font->font_driver, &font->font_data, font_path, font_size)) + { + RARCH_WARN("Couldn't initialize font renderer.\n"); + free(font); + return NULL; + } + + font->atlas = font->font_driver->get_atlas(font->font_data); + font->texture.sampler = d3d10->samplers[RARCH_FILTER_LINEAR][RARCH_WRAP_BORDER]; + font->texture.desc.Width = font->atlas->width; + font->texture.desc.Height = font->atlas->height; + font->texture.desc.Format = DXGI_FORMAT_A8_UNORM; + d3d10_init_texture(d3d10->device, &font->texture); + d3d10_update_texture( + d3d10->device, + font->atlas->width, font->atlas->height, font->atlas->width, + DXGI_FORMAT_A8_UNORM, font->atlas->buffer, &font->texture); + font->atlas->dirty = false; + + return font; +} + +static void d3d10_font_free_font(void* data, bool is_threaded) +{ + d3d10_font_t* font = (d3d10_font_t*)data; + + if (!font) + return; + + if (font->font_driver && font->font_data && font->font_driver->free) + font->font_driver->free(font->font_data); + + Release(font->texture.handle); + Release(font->texture.staging); + Release(font->texture.view); + free(font); +} + +static int d3d10_font_get_message_width(void* data, const char* msg, unsigned msg_len, float scale) +{ + d3d10_font_t* font = (d3d10_font_t*)data; + + unsigned i; + int delta_x = 0; + + if (!font) + return 0; + + for (i = 0; i < msg_len; i++) + { + const struct font_glyph *glyph; + const char* msg_tmp = &msg[i]; + unsigned code = utf8_walk(&msg_tmp); + unsigned skip = msg_tmp - &msg[i]; + + if (skip > 1) + i += skip - 1; + + glyph = font->font_driver->get_glyph(font->font_data, code); + + if (!glyph) /* Do something smarter here ... */ + glyph = font->font_driver->get_glyph(font->font_data, '?'); + + if (!glyph) + continue; + + delta_x += glyph->advance_x; + } + + return delta_x * scale; +} + +static void d3d10_font_render_line( + video_frame_info_t* video_info, + d3d10_font_t* font, + const char* msg, + unsigned msg_len, + float scale, + const unsigned int color, + float pos_x, + float pos_y, + unsigned text_align) +{ + unsigned i, count; + D3D10_MAPPED_SUBRESOURCE mapped_vbo; + d3d10_sprite_t* v; + d3d10_video_t* d3d10 = (d3d10_video_t*)video_info->userdata; + unsigned width = video_info->width; + unsigned height = video_info->height; + int x = roundf(pos_x * width); + int y = roundf((1.0 - pos_y) * height); + + if ( !d3d10 || + !d3d10->sprites.enabled || + msg_len > (unsigned)d3d10->sprites.capacity) + return; + + if (d3d10->sprites.offset + msg_len > (unsigned)d3d10->sprites.capacity) + d3d10->sprites.offset = 0; + + switch (text_align) + { + case TEXT_ALIGN_RIGHT: + x -= d3d10_font_get_message_width(font, msg, msg_len, scale); + break; + + case TEXT_ALIGN_CENTER: + x -= d3d10_font_get_message_width(font, msg, msg_len, scale) / 2; + break; + } + + D3D10MapBuffer(d3d10->sprites.vbo, D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); + v = (d3d10_sprite_t*)mapped_vbo.pData + d3d10->sprites.offset; + + for (i = 0; i < msg_len; i++) + { + const struct font_glyph* glyph; + const char* msg_tmp = &msg[i]; + unsigned code = utf8_walk(&msg_tmp); + unsigned skip = msg_tmp - &msg[i]; + + if (skip > 1) + i += skip - 1; + + glyph = font->font_driver->get_glyph(font->font_data, code); + + if (!glyph) /* Do something smarter here ... */ + glyph = font->font_driver->get_glyph(font->font_data, '?'); + + if (!glyph) + continue; + + v->pos.x = (x + glyph->draw_offset_x) * scale / (float)d3d10->viewport.Width; + v->pos.y = (y + glyph->draw_offset_y) * scale / (float)d3d10->viewport.Height; + v->pos.w = glyph->width * scale / (float)d3d10->viewport.Width; + v->pos.h = glyph->height * scale / (float)d3d10->viewport.Height; + + v->coords.u = glyph->atlas_offset_x / (float)font->texture.desc.Width; + v->coords.v = glyph->atlas_offset_y / (float)font->texture.desc.Height; + v->coords.w = glyph->width / (float)font->texture.desc.Width; + v->coords.h = glyph->height / (float)font->texture.desc.Height; + + v->params.scaling = 1; + v->params.rotation = 0; + + v->colors[0] = color; + v->colors[1] = color; + v->colors[2] = color; + v->colors[3] = color; + + v++; + + x += glyph->advance_x * scale; + y += glyph->advance_y * scale; + } + + count = v - ((d3d10_sprite_t*)mapped_vbo.pData + d3d10->sprites.offset); + D3D10UnmapBuffer(d3d10->sprites.vbo); + + if (!count) + return; + + if (font->atlas->dirty) + { + d3d10_update_texture( + d3d10->device, + font->atlas->width, font->atlas->height, font->atlas->width, + DXGI_FORMAT_A8_UNORM, font->atlas->buffer, &font->texture); + font->atlas->dirty = false; + } + + d3d10_set_texture_and_sampler(d3d10->device, 0, &font->texture); + D3D10SetBlendState(d3d10->device, d3d10->blend_enable, NULL, D3D10_DEFAULT_SAMPLE_MASK); + + D3D10SetPShader(d3d10->device, d3d10->sprites.shader_font.ps); + D3D10Draw(d3d10->device, count, d3d10->sprites.offset); + D3D10SetPShader(d3d10->device, d3d10->sprites.shader.ps); + + d3d10->sprites.offset += count; +} + +static void d3d10_font_render_message( + video_frame_info_t* video_info, + d3d10_font_t* font, + const char* msg, + float scale, + const unsigned int color, + float pos_x, + float pos_y, + unsigned text_align) +{ + int lines = 0; + float line_height; + + if (!msg || !*msg) + return; + + /* If the font height is not supported just draw as usual */ + if (!font->font_driver->get_line_height) + { + d3d10_font_render_line( + video_info, font, msg, strlen(msg), scale, color, pos_x, pos_y, text_align); + return; + } + + line_height = font->font_driver->get_line_height(font->font_data) * scale / video_info->height; + + for (;;) + { + const char* delim = strchr(msg, '\n'); + + /* Draw the line */ + if (delim) + { + unsigned msg_len = delim - msg; + d3d10_font_render_line( + video_info, font, msg, msg_len, scale, color, pos_x, + pos_y - (float)lines * line_height, text_align); + msg += msg_len + 1; + lines++; + } + else + { + unsigned msg_len = strlen(msg); + d3d10_font_render_line( + video_info, font, msg, msg_len, scale, color, pos_x, + pos_y - (float)lines * line_height, text_align); + break; + } + } +} + +static void d3d10_font_render_msg( + video_frame_info_t* video_info, void* data, + const char* msg, const struct font_params *params) +{ + float x, y, scale, drop_mod, drop_alpha; + int drop_x, drop_y; + enum text_alignment text_align; + unsigned color, color_dark, r, g, b, + alpha, r_dark, g_dark, b_dark, alpha_dark; + d3d10_font_t* font = (d3d10_font_t*)data; + unsigned width = video_info->width; + unsigned height = video_info->height; + + if (!font || !msg || !*msg) + return; + + if (params) + { + x = params->x; + y = params->y; + scale = params->scale; + text_align = params->text_align; + drop_x = params->drop_x; + drop_y = params->drop_y; + drop_mod = params->drop_mod; + drop_alpha = params->drop_alpha; + + r = FONT_COLOR_GET_RED(params->color); + g = FONT_COLOR_GET_GREEN(params->color); + b = FONT_COLOR_GET_BLUE(params->color); + alpha = FONT_COLOR_GET_ALPHA(params->color); + + color = DXGI_COLOR_RGBA(r, g, b, alpha); + } + else + { + x = video_info->font_msg_pos_x; + y = video_info->font_msg_pos_y; + scale = 1.0f; + text_align = TEXT_ALIGN_LEFT; + + r = (video_info->font_msg_color_r * 255); + g = (video_info->font_msg_color_g * 255); + b = (video_info->font_msg_color_b * 255); + alpha = 255; + color = DXGI_COLOR_RGBA(r, g, b, alpha); + + drop_x = -2; + drop_y = -2; + drop_mod = 0.3f; + drop_alpha = 1.0f; + } + + if (drop_x || drop_y) + { + r_dark = r * drop_mod; + g_dark = g * drop_mod; + b_dark = b * drop_mod; + alpha_dark = alpha * drop_alpha; + color_dark = DXGI_COLOR_RGBA(r_dark, g_dark, b_dark, alpha_dark); + + d3d10_font_render_message( + video_info, font, msg, scale, color_dark, + x + scale * drop_x / width, + y + scale * drop_y / height, text_align); + } + + d3d10_font_render_message(video_info, font, msg, scale, + color, x, y, text_align); +} + +static const struct font_glyph* d3d10_font_get_glyph(void *data, uint32_t code) +{ + d3d10_font_t* font = (d3d10_font_t*)data; + + if (!font || !font->font_driver) + return NULL; + + if (!font->font_driver->ident) + return NULL; + + return font->font_driver->get_glyph((void*)font->font_driver, code); +} + +static void d3d10_font_bind_block(void* data, void *userdata) +{ + (void)data; +} + +font_renderer_t d3d10_font = { + d3d10_font_init_font, + d3d10_font_free_font, + d3d10_font_render_msg, + "d3d10font", + d3d10_font_get_glyph, + d3d10_font_bind_block, + NULL, /* flush */ + d3d10_font_get_message_width, +}; diff --git a/gfx/font_driver.c b/gfx/font_driver.c index 75a5adb2f5..28080b80a5 100644 --- a/gfx/font_driver.c +++ b/gfx/font_driver.c @@ -310,6 +310,37 @@ static bool vulkan_font_init_first( } #endif +#ifdef HAVE_D3D10 +static const font_renderer_t *d3d10_font_backends[] = { + &d3d10_font, + NULL, +}; + +static bool d3d10_font_init_first( + const void **font_driver, void **font_handle, + void *video_data, const char *font_path, + float font_size, bool is_threaded) +{ + unsigned i; + + for (i = 0; d3d10_font_backends[i]; i++) + { + void *data = d3d10_font_backends[i]->init(video_data, + font_path, font_size, + is_threaded); + + if (!data) + continue; + + *font_driver = d3d10_font_backends[i]; + *font_handle = data; + return true; + } + + return false; +} +#endif + #ifdef HAVE_D3D11 static const font_renderer_t *d3d11_font_backends[] = { &d3d11_font, @@ -493,6 +524,11 @@ static bool font_init_first( return d3d9_font_init_first(font_driver, font_handle, video_data, font_path, font_size, is_threaded); #endif +#ifdef HAVE_D3D10 + case FONT_DRIVER_RENDER_D3D10_API: + return d3d10_font_init_first(font_driver, font_handle, + video_data, font_path, font_size, is_threaded); +#endif #ifdef HAVE_D3D11 case FONT_DRIVER_RENDER_D3D11_API: return d3d11_font_init_first(font_driver, font_handle, diff --git a/gfx/font_driver.h b/gfx/font_driver.h index 939eccb951..7d1818b4a3 100644 --- a/gfx/font_driver.h +++ b/gfx/font_driver.h @@ -163,6 +163,7 @@ extern font_renderer_t vita2d_vita_font; extern font_renderer_t ctr_font; extern font_renderer_t wiiu_font; extern font_renderer_t vulkan_raster_font; +extern font_renderer_t d3d10_font; extern font_renderer_t d3d11_font; extern font_renderer_t d3d12_font; extern font_renderer_t caca_font; diff --git a/gfx/video_defines.h b/gfx/video_defines.h index 96437c0590..997850b856 100644 --- a/gfx/video_defines.h +++ b/gfx/video_defines.h @@ -84,6 +84,7 @@ enum font_driver_render_api FONT_DRIVER_RENDER_OPENGL_API, FONT_DRIVER_RENDER_D3D8_API, FONT_DRIVER_RENDER_D3D9_API, + FONT_DRIVER_RENDER_D3D10_API, FONT_DRIVER_RENDER_D3D11_API, FONT_DRIVER_RENDER_D3D12_API, FONT_DRIVER_RENDER_VITA2D, diff --git a/griffin/griffin.c b/griffin/griffin.c index 81f8f00a13..d2b12c0674 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -510,6 +510,10 @@ FONTS #include "../gfx/drivers_font/vulkan_raster_font.c" #endif +#if defined(HAVE_D3D10) +#include "../gfx/drivers_font/d3d10_font.c" +#endif + #if defined(HAVE_D3D11) #include "../gfx/drivers_font/d3d11_font.c" #endif @@ -1169,6 +1173,10 @@ MENU #include "../menu/drivers_display/menu_display_d3d9.c" #endif +#if defined(HAVE_D3D10) +#include "../menu/drivers_display/menu_display_d3d10.c" +#endif + #if defined(HAVE_D3D11) #include "../menu/drivers_display/menu_display_d3d11.c" #endif diff --git a/menu/drivers_display/menu_display_d3d10.c b/menu/drivers_display/menu_display_d3d10.c new file mode 100644 index 0000000000..d5e200a638 --- /dev/null +++ b/menu/drivers_display/menu_display_d3d10.c @@ -0,0 +1,289 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2018 - Daniel De Matteis + * Copyright (C) 2014-2018 - Ali Bouhlel + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#define CINTERFACE + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../menu_driver.h" + +#include "../../retroarch.h" +#include "../../gfx/font_driver.h" +#include "../../gfx/video_driver.h" +#include "../../gfx/common/d3d10_common.h" + +static const float* menu_display_d3d10_get_default_vertices(void) +{ + return NULL; +} + +static const float* menu_display_d3d10_get_default_tex_coords(void) +{ + return NULL; +} + +static void* menu_display_d3d10_get_default_mvp(video_frame_info_t *video_info) +{ + return NULL; +} + +static void menu_display_d3d10_blend_begin(video_frame_info_t *video_info) +{ + d3d10_video_t* d3d10 = video_info ? (d3d10_video_t*)video_info->userdata : NULL; + D3D10SetBlendState(d3d10->device, + d3d10->blend_enable, NULL, D3D10_DEFAULT_SAMPLE_MASK); +} + +static void menu_display_d3d10_blend_end(video_frame_info_t *video_info) +{ + d3d10_video_t* d3d10 = video_info ? (d3d10_video_t*)video_info->userdata : NULL; + D3D10SetBlendState(d3d10->device, + d3d10->blend_disable, NULL, D3D10_DEFAULT_SAMPLE_MASK); +} + +static void menu_display_d3d10_viewport(void* data, video_frame_info_t *video_info) +{ +} + +static void menu_display_d3d10_draw(void* data, video_frame_info_t *video_info) +{ + int vertex_count; + d3d10_video_t* d3d10 = video_info ? (d3d10_video_t*)video_info->userdata : NULL; + menu_display_ctx_draw_t* draw = (menu_display_ctx_draw_t*)data; + + if (!d3d10 || !draw || !draw->texture) + return; + + switch (draw->pipeline.id) + { + case VIDEO_SHADER_MENU: + case VIDEO_SHADER_MENU_2: + case VIDEO_SHADER_MENU_3: + case VIDEO_SHADER_MENU_4: + case VIDEO_SHADER_MENU_5: + case VIDEO_SHADER_MENU_6: + d3d10_set_shader(d3d10->device, &d3d10->shaders[draw->pipeline.id]); + D3D10Draw(d3d10->device, draw->coords->vertices, 0); + + D3D10SetBlendState(d3d10->device, d3d10->blend_enable, NULL, D3D10_DEFAULT_SAMPLE_MASK); + d3d10_set_shader(d3d10->device, &d3d10->sprites.shader); + D3D10SetVertexBuffer(d3d10->device, 0, d3d10->sprites.vbo, sizeof(d3d10_sprite_t), 0); + D3D10SetPrimitiveTopology(d3d10->device, D3D10_PRIMITIVE_TOPOLOGY_POINTLIST); + return; + } + + if (draw->coords->vertex && draw->coords->tex_coord && draw->coords->color) + vertex_count = draw->coords->vertices; + else + vertex_count = 1; + + if (!d3d10->sprites.enabled || vertex_count > d3d10->sprites.capacity) + return; + + if (d3d10->sprites.offset + vertex_count > d3d10->sprites.capacity) + d3d10->sprites.offset = 0; + + { + D3D10_MAPPED_SUBRESOURCE mapped_vbo; + d3d10_sprite_t* sprite = NULL; + + D3D10MapBuffer(d3d10->sprites.vbo, D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); + + sprite = (d3d10_sprite_t*)mapped_vbo.pData + d3d10->sprites.offset; + + if (vertex_count == 1) + { + sprite->pos.x = draw->x / (float)d3d10->viewport.Width; + sprite->pos.y = + (d3d10->viewport.Height - draw->y - draw->height) / (float)d3d10->viewport.Height; + sprite->pos.w = draw->width / (float)d3d10->viewport.Width; + sprite->pos.h = draw->height / (float)d3d10->viewport.Height; + + sprite->coords.u = 0.0f; + sprite->coords.v = 0.0f; + sprite->coords.w = 1.0f; + sprite->coords.h = 1.0f; + + if (draw->scale_factor) + sprite->params.scaling = draw->scale_factor; + else + sprite->params.scaling = 1.0f; + + sprite->params.rotation = draw->rotation; + + sprite->colors[3] = DXGI_COLOR_RGBA( + 0xFF * draw->coords->color[0], 0xFF * draw->coords->color[1], + 0xFF * draw->coords->color[2], 0xFF * draw->coords->color[3]); + sprite->colors[2] = DXGI_COLOR_RGBA( + 0xFF * draw->coords->color[4], 0xFF * draw->coords->color[5], + 0xFF * draw->coords->color[6], 0xFF * draw->coords->color[7]); + sprite->colors[1] = DXGI_COLOR_RGBA( + 0xFF * draw->coords->color[8], 0xFF * draw->coords->color[9], + 0xFF * draw->coords->color[10], 0xFF * draw->coords->color[11]); + sprite->colors[0] = DXGI_COLOR_RGBA( + 0xFF * draw->coords->color[12], 0xFF * draw->coords->color[13], + 0xFF * draw->coords->color[14], 0xFF * draw->coords->color[15]); + } + else + { + int i; + const float* vertex = draw->coords->vertex; + const float* tex_coord = draw->coords->tex_coord; + const float* color = draw->coords->color; + + for (i = 0; i < vertex_count; i++) + { + d3d10_vertex_t* v = (d3d10_vertex_t*)sprite; + v->position[0] = *vertex++; + v->position[1] = *vertex++; + v->texcoord[0] = *tex_coord++; + v->texcoord[1] = *tex_coord++; + v->color[0] = *color++; + v->color[1] = *color++; + v->color[2] = *color++; + v->color[3] = *color++; + + sprite++; + } + + d3d10_set_shader(d3d10->device, &d3d10->shaders[VIDEO_SHADER_STOCK_BLEND]); + D3D10SetPrimitiveTopology(d3d10->device, D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + } + + D3D10UnmapBuffer(d3d10->sprites.vbo); + } + + d3d10_set_texture_and_sampler(d3d10->device, 0, (d3d10_texture_t*)draw->texture); + D3D10Draw(d3d10->device, vertex_count, d3d10->sprites.offset); + d3d10->sprites.offset += vertex_count; + + if (vertex_count > 1) + { + d3d10_set_shader(d3d10->device, &d3d10->sprites.shader); + D3D10SetPrimitiveTopology(d3d10->device, D3D10_PRIMITIVE_TOPOLOGY_POINTLIST); + } + + return; +} + +static void menu_display_d3d10_draw_pipeline(void* data, + video_frame_info_t *video_info) +{ + menu_display_ctx_draw_t* draw = (menu_display_ctx_draw_t*)data; + d3d10_video_t* d3d10 = video_info ? (d3d10_video_t*)video_info->userdata : NULL; + + if (!d3d10 || !draw) + return; + + switch (draw->pipeline.id) + { + case VIDEO_SHADER_MENU: + case VIDEO_SHADER_MENU_2: + { + video_coord_array_t* ca = menu_display_get_coords_array(); + + if (!d3d10->menu_pipeline_vbo) + { + D3D10_BUFFER_DESC desc = { 0 }; + desc.Usage = D3D10_USAGE_IMMUTABLE; + desc.ByteWidth = ca->coords.vertices * 2 * sizeof(float); + desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; + + D3D10_SUBRESOURCE_DATA vertexData = { ca->coords.vertex }; + D3D10CreateBuffer(d3d10->device, &desc, &vertexData, &d3d10->menu_pipeline_vbo); + } + D3D10SetVertexBuffer(d3d10->device, 0, d3d10->menu_pipeline_vbo, 2 * sizeof(float), 0); + draw->coords->vertices = ca->coords.vertices; + D3D10SetBlendState(d3d10->device, d3d10->blend_pipeline, NULL, D3D10_DEFAULT_SAMPLE_MASK); + break; + } + + case VIDEO_SHADER_MENU_3: + case VIDEO_SHADER_MENU_4: + case VIDEO_SHADER_MENU_5: + case VIDEO_SHADER_MENU_6: + D3D10SetVertexBuffer(d3d10->device, 0, d3d10->frame.vbo, sizeof(d3d10_vertex_t), 0); + draw->coords->vertices = 4; + break; + default: + return; + } + + D3D10SetPrimitiveTopology(d3d10->device, D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + d3d10->ubo_values.time += 0.01f; + + { + D3D10_MAPPED_SUBRESOURCE mapped_ubo; + D3D10MapBuffer(d3d10->ubo, D3D10_MAP_WRITE_DISCARD, 0, (void**)&mapped_ubo); + *(d3d10_uniform_t*)mapped_ubo.pData = d3d10->ubo_values; + D3D10UnmapBuffer(d3d10->ubo); + } +} + +static void menu_display_d3d10_restore_clear_color(void) {} + +static void menu_display_d3d10_clear_color( + menu_display_ctx_clearcolor_t* clearcolor, + video_frame_info_t *video_info) +{ + d3d10_video_t *d3d10 = video_info ? + (d3d10_video_t*)video_info->userdata : NULL; + + if (!d3d10 || !clearcolor) + return; + + D3D10ClearRenderTargetView(d3d10->device, + d3d10->renderTargetView, (float*)clearcolor); +} + +static bool menu_display_d3d10_font_init_first( + void** font_handle, + void* video_data, + const char* font_path, + float font_size, + bool is_threaded) +{ + font_data_t** handle = (font_data_t**)font_handle; + font_data_t* new_handle = font_driver_init_first( + video_data, font_path, font_size, true, + is_threaded, FONT_DRIVER_RENDER_D3D10_API); + if (!new_handle) + return false; + *handle = new_handle; + return true; +} + +menu_display_ctx_driver_t menu_display_ctx_d3d10 = { + menu_display_d3d10_draw, + menu_display_d3d10_draw_pipeline, + menu_display_d3d10_viewport, + menu_display_d3d10_blend_begin, + menu_display_d3d10_blend_end, + menu_display_d3d10_restore_clear_color, + menu_display_d3d10_clear_color, + menu_display_d3d10_get_default_mvp, + menu_display_d3d10_get_default_vertices, + menu_display_d3d10_get_default_tex_coords, + menu_display_d3d10_font_init_first, + MENU_VIDEO_DRIVER_DIRECT3D10, + "menu_display_d3d10", + true +}; diff --git a/menu/menu_driver.c b/menu/menu_driver.c index ddbadabe02..c9ba81d516 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -236,6 +236,10 @@ static bool menu_display_check_compatibility( if (string_is_equal(video_driver, "d3d9")) return true; break; + case MENU_VIDEO_DRIVER_DIRECT3D10: + if (string_is_equal(video_driver, "d3d10")) + return true; + break; case MENU_VIDEO_DRIVER_DIRECT3D11: if (string_is_equal(video_driver, "d3d11")) return true; diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 412e920478..f5e37241c9 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -285,6 +285,7 @@ enum menu_display_driver_type MENU_VIDEO_DRIVER_VULKAN, MENU_VIDEO_DRIVER_DIRECT3D8, MENU_VIDEO_DRIVER_DIRECT3D9, + MENU_VIDEO_DRIVER_DIRECT3D10, MENU_VIDEO_DRIVER_DIRECT3D11, MENU_VIDEO_DRIVER_DIRECT3D12, MENU_VIDEO_DRIVER_VITA2D, From 8e52fcff96234f40cd24bb5db75ad5d89100d42a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 03:30:34 +0200 Subject: [PATCH 338/517] Add D3D10 entry --- menu/menu_driver.c | 3 +++ menu/menu_driver.h | 1 + 2 files changed, 4 insertions(+) diff --git a/menu/menu_driver.c b/menu/menu_driver.c index c9ba81d516..2169895519 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -99,6 +99,9 @@ static menu_display_ctx_driver_t *menu_display_ctx_drivers[] = { #ifdef HAVE_D3D9 &menu_display_ctx_d3d9, #endif +#ifdef HAVE_D3D10 + &menu_display_ctx_d3d10, +#endif #ifdef HAVE_D3D11 &menu_display_ctx_d3d11, #endif diff --git a/menu/menu_driver.h b/menu/menu_driver.h index f5e37241c9..6cf794de48 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -754,6 +754,7 @@ extern menu_display_ctx_driver_t menu_display_ctx_gl; extern menu_display_ctx_driver_t menu_display_ctx_vulkan; extern menu_display_ctx_driver_t menu_display_ctx_d3d8; extern menu_display_ctx_driver_t menu_display_ctx_d3d9; +extern menu_display_ctx_driver_t menu_display_ctx_d3d10; extern menu_display_ctx_driver_t menu_display_ctx_d3d11; extern menu_display_ctx_driver_t menu_display_ctx_d3d12; extern menu_display_ctx_driver_t menu_display_ctx_vita2d; From 7515c3d7c393c37a8104ac22473fc638bfe79c08 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 03:33:08 +0200 Subject: [PATCH 339/517] (D3D10) Switch to shader model 4.0 --- gfx/common/d3d10_common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gfx/common/d3d10_common.c b/gfx/common/d3d10_common.c index 12adaf5493..d47474eb17 100644 --- a/gfx/common/d3d10_common.c +++ b/gfx/common/d3d10_common.c @@ -186,20 +186,20 @@ bool d3d10_init_shader( if (!src) /* LPCWSTR filename */ { - if (vs_entry && !d3d_compile_from_file((LPCWSTR)src_name, vs_entry, "vs_5_0", &vs_code)) + if (vs_entry && !d3d_compile_from_file((LPCWSTR)src_name, vs_entry, "vs_4_0", &vs_code)) success = false; - if (ps_entry && !d3d_compile_from_file((LPCWSTR)src_name, ps_entry, "ps_5_0", &ps_code)) + if (ps_entry && !d3d_compile_from_file((LPCWSTR)src_name, ps_entry, "ps_4_0", &ps_code)) success = false; - if (gs_entry && !d3d_compile_from_file((LPCWSTR)src_name, gs_entry, "gs_5_0", &gs_code)) + if (gs_entry && !d3d_compile_from_file((LPCWSTR)src_name, gs_entry, "gs_4_0", &gs_code)) success = false; } else /* char array */ { - if (vs_entry && !d3d_compile(src, size, (LPCSTR)src_name, vs_entry, "vs_5_0", &vs_code)) + if (vs_entry && !d3d_compile(src, size, (LPCSTR)src_name, vs_entry, "vs_4_0", &vs_code)) success = false; - if (ps_entry && !d3d_compile(src, size, (LPCSTR)src_name, ps_entry, "ps_5_0", &ps_code)) + if (ps_entry && !d3d_compile(src, size, (LPCSTR)src_name, ps_entry, "ps_4_0", &ps_code)) success = false; - if (gs_entry && !d3d_compile(src, size, (LPCSTR)src_name, gs_entry, "gs_5_0", &gs_code)) + if (gs_entry && !d3d_compile(src, size, (LPCSTR)src_name, gs_entry, "gs_4_0", &gs_code)) success = false; } From b78dcf9f4cd974b7bde9e7f185ea9ef838d3c028 Mon Sep 17 00:00:00 2001 From: aliaspider Date: Sat, 21 Apr 2018 03:56:19 +0100 Subject: [PATCH 340/517] update D3D10 driver. --- gfx/common/d3d10_common.c | 58 +++- gfx/common/d3d10_common.h | 9 - gfx/drivers/d3d10.c | 366 +++++++++++----------- gfx/drivers_font/d3d10_font.c | 6 +- menu/drivers_display/menu_display_d3d10.c | 12 +- 5 files changed, 228 insertions(+), 223 deletions(-) diff --git a/gfx/common/d3d10_common.c b/gfx/common/d3d10_common.c index d47474eb17..bca16016b3 100644 --- a/gfx/common/d3d10_common.c +++ b/gfx/common/d3d10_common.c @@ -68,40 +68,68 @@ HRESULT WINAPI D3D10CreateDeviceAndSwapChain( void d3d10_init_texture(D3D10Device device, d3d10_texture_t* texture) { - Release(texture->handle); - Release(texture->staging); - Release(texture->view); + bool is_render_target = texture->desc.BindFlags & D3D10_BIND_RENDER_TARGET; + UINT format_support = D3D10_FORMAT_SUPPORT_TEXTURE2D | D3D10_FORMAT_SUPPORT_SHADER_SAMPLE; - // .Usage = D3D10_USAGE_DYNAMIC, - // .CPUAccessFlags = D3D10_CPU_ACCESS_WRITE, + d3d10_release_texture(texture); texture->desc.MipLevels = 1; texture->desc.ArraySize = 1; texture->desc.SampleDesc.Count = 1; texture->desc.SampleDesc.Quality = 0; - texture->desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; - texture->desc.CPUAccessFlags = 0; - texture->desc.MiscFlags = 0; + texture->desc.BindFlags |= D3D10_BIND_SHADER_RESOURCE; + texture->desc.CPUAccessFlags = + texture->desc.Usage == D3D10_USAGE_DYNAMIC ? D3D10_CPU_ACCESS_WRITE : 0; + + if (texture->desc.MiscFlags & D3D10_RESOURCE_MISC_GENERATE_MIPS) + { + unsigned width, height; + + texture->desc.BindFlags |= D3D10_BIND_RENDER_TARGET; + width = texture->desc.Width >> 5; + height = texture->desc.Height >> 5; + + while (width && height) + { + width >>= 1; + height >>= 1; + texture->desc.MipLevels++; + } + } + + if (texture->desc.BindFlags & D3D10_BIND_RENDER_TARGET) + format_support |= D3D10_FORMAT_SUPPORT_RENDER_TARGET; + + texture->desc.Format = d3d10_get_closest_match(device, texture->desc.Format, format_support); + D3D10CreateTexture2D(device, &texture->desc, NULL, &texture->handle); { D3D10_SHADER_RESOURCE_VIEW_DESC view_desc = { DXGI_FORMAT_UNKNOWN }; - view_desc.Format = texture->desc.Format; - view_desc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D; - view_desc.Texture2D.MostDetailedMip = 0; - view_desc.Texture2D.MipLevels = -1; - - D3D10CreateTexture2DShaderResourceView(device, - texture->handle, &view_desc, &texture->view); + view_desc.Format = texture->desc.Format; + view_desc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D; + view_desc.Texture2D.MostDetailedMip = 0; + view_desc.Texture2D.MipLevels = -1; + D3D10CreateTexture2DShaderResourceView(device, texture->handle, &view_desc, &texture->view); } + if (is_render_target) + D3D10CreateTexture2DRenderTargetView(device, texture->handle, NULL, &texture->rt_view); + else { D3D10_TEXTURE2D_DESC desc = texture->desc; + desc.MipLevels = 1; desc.BindFlags = 0; + desc.MiscFlags = 0; desc.Usage = D3D10_USAGE_STAGING; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; D3D10CreateTexture2D(device, &desc, NULL, &texture->staging); } + + texture->size_data.x = texture->desc.Width; + texture->size_data.y = texture->desc.Height; + texture->size_data.z = 1.0f / texture->desc.Width; + texture->size_data.w = 1.0f / texture->desc.Height; } void d3d10_update_texture( diff --git a/gfx/common/d3d10_common.h b/gfx/common/d3d10_common.h index bff18cb40f..ee8373ca57 100644 --- a/gfx/common/d3d10_common.h +++ b/gfx/common/d3d10_common.h @@ -22,8 +22,6 @@ #include "../drivers_shader/slang_process.h" -typedef D3D10_MAPPED_TEXTURE3D D3D10_MAPPED_SUBRESOURCE; - typedef const ID3D10SamplerState* D3D10SamplerStateRef; typedef ID3D10InputLayout* D3D10InputLayout; @@ -1141,11 +1139,6 @@ typedef struct D3D10Buffer ubo; d3d10_uniform_t ubo_values; D3D10SamplerState samplers[RARCH_FILTER_MAX][RARCH_WRAP_MAX]; - D3D10InputLayout layout; - D3D10VertexShader vs; - D3D10PixelShader ps; - D3D10SamplerState sampler_nearest; - D3D10SamplerState sampler_linear; D3D10BlendState blend_enable; D3D10BlendState blend_disable; D3D10BlendState blend_pipeline; @@ -1188,7 +1181,6 @@ typedef struct { d3d10_texture_t texture; D3D10Buffer vbo; - D3D10SamplerState sampler; bool enabled; bool fullscreen; } menu; @@ -1197,7 +1189,6 @@ typedef struct d3d10_texture_t texture[GFX_MAX_FRAME_HISTORY + 1]; D3D10Buffer vbo; D3D10Buffer ubo; - D3D10SamplerState sampler; D3D10_VIEWPORT viewport; float4_t output_size; int rotation; diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 98e4791fd8..c7e3e7890c 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -46,71 +46,60 @@ static void d3d10_free_overlays(d3d10_video_t* d3d10) static void d3d10_overlay_vertex_geom(void* data, unsigned index, float x, float y, float w, float h) { - D3D10_MAPPED_SUBRESOURCE mapped_vbo; - d3d10_video_t* d3d10 = (d3d10_video_t*)data; + d3d10_sprite_t* sprites = NULL; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; if (!d3d10) return; D3D10MapBuffer(d3d10->overlays.vbo, - D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); - { - d3d10_sprite_t* sprites = (d3d10_sprite_t*)mapped_vbo.pData; - sprites[index].pos.x = x; - sprites[index].pos.y = y; - sprites[index].pos.w = w; - sprites[index].pos.h = h; - } + D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&sprites); + sprites[index].pos.x = x; + sprites[index].pos.y = y; + sprites[index].pos.w = w; + sprites[index].pos.h = h; D3D10UnmapBuffer(d3d10->overlays.vbo); } static void d3d10_overlay_tex_geom(void* data, unsigned index, float u, float v, float w, float h) { - D3D10_MAPPED_SUBRESOURCE mapped_vbo; - d3d10_video_t* d3d10 = (d3d10_video_t*)data; + d3d10_sprite_t* sprites = NULL; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; if (!d3d10) return; D3D10MapBuffer( d3d10->overlays.vbo, - D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); - { - d3d10_sprite_t* sprites = (d3d10_sprite_t*)mapped_vbo.pData; - sprites[index].coords.u = u; - sprites[index].coords.v = v; - sprites[index].coords.w = w; - sprites[index].coords.h = h; - } + D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&sprites); + sprites[index].coords.u = u; + sprites[index].coords.v = v; + sprites[index].coords.w = w; + sprites[index].coords.h = h; D3D10UnmapBuffer(d3d10->overlays.vbo); } static void d3d10_overlay_set_alpha(void* data, unsigned index, float mod) { - D3D10_MAPPED_SUBRESOURCE mapped_vbo; - d3d10_video_t* d3d10 = (d3d10_video_t*)data; + d3d10_sprite_t* sprites = NULL; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; if (!d3d10) return; D3D10MapBuffer( - d3d10->overlays.vbo, - D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); - - { - d3d10_sprite_t* sprites = (d3d10_sprite_t*)mapped_vbo.pData; - sprites[index].colors[0] = DXGI_COLOR_RGBA(0xFF, 0xFF, 0xFF, mod * 0xFF); - sprites[index].colors[1] = sprites[index].colors[0]; - sprites[index].colors[2] = sprites[index].colors[0]; - sprites[index].colors[3] = sprites[index].colors[0]; - } + d3d10->overlays.vbo, + D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&sprites); + sprites[index].colors[0] = DXGI_COLOR_RGBA(0xFF, 0xFF, 0xFF, mod * 0xFF); + sprites[index].colors[1] = sprites[index].colors[0]; + sprites[index].colors[2] = sprites[index].colors[0]; + sprites[index].colors[3] = sprites[index].colors[0]; D3D10UnmapBuffer(d3d10->overlays.vbo); } static bool d3d10_overlay_load(void* data, const void* image_data, unsigned num_images) { D3D10_BUFFER_DESC desc; - D3D10_MAPPED_SUBRESOURCE mapped_vbo; int i; d3d10_sprite_t* sprites; d3d10_video_t* d3d10 = (d3d10_video_t*)data; @@ -135,8 +124,7 @@ static bool d3d10_overlay_load(void* data, const void* image_data, unsigned num_ D3D10CreateBuffer(d3d10->device, &desc, NULL, &d3d10->overlays.vbo); D3D10MapBuffer(d3d10->overlays.vbo, - D3D10_MAP_WRITE_DISCARD, 0, (void**)&mapped_vbo); - sprites = (d3d10_sprite_t*)mapped_vbo.pData; + D3D10_MAP_WRITE_DISCARD, 0, (void**)&sprites); for (i = 0; i < num_images; i++) { @@ -210,31 +198,33 @@ static void d3d10_get_overlay_interface(void* data, const video_overlay_interfac static void d3d10_set_filtering(void* data, unsigned index, bool smooth) { + unsigned i; d3d10_video_t* d3d10 = (d3d10_video_t*)data; - if (smooth) - d3d10->frame.sampler = d3d10->sampler_linear; - else - d3d10->frame.sampler = d3d10->sampler_nearest; + for (i = 0; i < RARCH_WRAP_MAX; i++) + { + if (smooth) + d3d10->samplers[RARCH_FILTER_UNSPEC][i] = d3d10->samplers[RARCH_FILTER_LINEAR][i]; + else + d3d10->samplers[RARCH_FILTER_UNSPEC][i] = d3d10->samplers[RARCH_FILTER_NEAREST][i]; + } } + static void d3d10_gfx_set_rotation(void* data, unsigned rotation) { - math_matrix_4x4 rot; - math_matrix_4x4* mvp; - d3d10_video_t* d3d10 = (d3d10_video_t*)data; + math_matrix_4x4 rot; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; - if(!d3d10) + if (!d3d10) return; - d3d10->frame.rotation = rotation; + matrix_4x4_rotate_z(rot, rotation * (M_PI / 2.0f)); + matrix_4x4_multiply(d3d10->mvp, rot, d3d10->ubo_values.mvp); - - matrix_4x4_rotate_z(rot, d3d10->frame.rotation * (M_PI / 2.0f)); - matrix_4x4_multiply(d3d10->mvp, rot, d3d10->mvp_no_rot); - - D3D10MapBuffer(d3d10->frame.ubo, D3D10_MAP_WRITE_DISCARD, 0, (void**)&mvp); - *mvp = d3d10->mvp; + void* mapped_ubo; + D3D10MapBuffer(d3d10->frame.ubo, D3D10_MAP_WRITE_DISCARD, 0, &mapped_ubo); + *(math_matrix_4x4*)mapped_ubo = d3d10->mvp; D3D10UnmapBuffer(d3d10->frame.ubo); } @@ -244,13 +234,22 @@ static void d3d10_update_viewport(void* data, bool force_full) video_driver_update_viewport(&d3d10->vp, force_full, d3d10->keep_aspect); - d3d10->frame.viewport.TopLeftX = (float)d3d10->vp.x; - d3d10->frame.viewport.TopLeftY = (float)d3d10->vp.y; - d3d10->frame.viewport.Width = (float)d3d10->vp.width; - d3d10->frame.viewport.Height = (float)d3d10->vp.height; + d3d10->frame.viewport.TopLeftX = d3d10->vp.x; + d3d10->frame.viewport.TopLeftY = d3d10->vp.y; + d3d10->frame.viewport.Width = d3d10->vp.width; + d3d10->frame.viewport.Height = d3d10->vp.height; d3d10->frame.viewport.MaxDepth = 0.0f; d3d10->frame.viewport.MaxDepth = 1.0f; + if (d3d10->shader_preset && (d3d10->frame.output_size.x != d3d10->vp.width || + d3d10->frame.output_size.y != d3d10->vp.height)) + d3d10->resize_render_targets = true; + + d3d10->frame.output_size.x = d3d10->vp.width; + d3d10->frame.output_size.y = d3d10->vp.height; + d3d10->frame.output_size.z = 1.0f / d3d10->vp.width; + d3d10->frame.output_size.w = 1.0f / d3d10->vp.height; + d3d10->resize_viewport = false; } @@ -299,72 +298,6 @@ static void d3d10_free_shader_preset(d3d10_video_t* d3d10) d3d10->resize_render_targets = false; } - -static void d3d10_gfx_free(void* data) -{ - unsigned i; - d3d10_video_t* d3d10 = (d3d10_video_t*)data; - - if (!d3d10) - return; - -#ifdef HAVE_OVERLAY - d3d10_free_overlays(d3d10); -#endif - - d3d10_free_shader_preset(d3d10); - - d3d10_release_texture(&d3d10->frame.texture[0]); - Release(d3d10->frame.ubo); - Release(d3d10->frame.vbo); - - d3d10_release_texture(&d3d10->menu.texture); - Release(d3d10->menu.vbo); - - d3d10_release_shader(&d3d10->sprites.shader); - d3d10_release_shader(&d3d10->sprites.shader_font); - Release(d3d10->sprites.vbo); - - for (i = 0; i < GFX_MAX_SHADERS; i++) - d3d10_release_shader(&d3d10->shaders[i]); - - Release(d3d10->menu_pipeline_vbo); - Release(d3d10->blend_pipeline); - - Release(d3d10->ubo); - - Release(d3d10->blend_enable); - Release(d3d10->blend_disable); - - for (i = 0; i < RARCH_WRAP_MAX; i++) - { - Release(d3d10->samplers[RARCH_FILTER_LINEAR][i]); - Release(d3d10->samplers[RARCH_FILTER_NEAREST][i]); - } - - Release(d3d10->state); - Release(d3d10->renderTargetView); - Release(d3d10->swapChain); - - font_driver_free_osd(); - -#if 0 - if (video_driver_is_video_cache_context()) - { - cached_device_d3d10 = d3d10->device; - cached_context = d3d10->context; - } - else -#endif - { - Release(d3d10->device); - } - - win32_monitor_from_window(); - win32_destroy_window(); - free(d3d10); -} - static bool d3d10_gfx_set_shader(void* data, enum rarch_shader_type type, const char* path) { @@ -513,9 +446,6 @@ static bool d3d10_gfx_set_shader(void* data, desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; desc.MiscFlags = 0; -#if 0 - desc.StructureByteStride = 0; -#endif if (!desc.ByteWidth) continue; @@ -564,9 +494,75 @@ error: return false; } +static void d3d10_gfx_free(void* data) +{ + unsigned i; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (!d3d10) + return; + +#ifdef HAVE_OVERLAY + d3d10_free_overlays(d3d10); +#endif + + d3d10_free_shader_preset(d3d10); + + d3d10_release_texture(&d3d10->frame.texture[0]); + Release(d3d10->frame.ubo); + Release(d3d10->frame.vbo); + + d3d10_release_texture(&d3d10->menu.texture); + Release(d3d10->menu.vbo); + + d3d10_release_shader(&d3d10->sprites.shader); + d3d10_release_shader(&d3d10->sprites.shader_font); + Release(d3d10->sprites.vbo); + + for (i = 0; i < GFX_MAX_SHADERS; i++) + d3d10_release_shader(&d3d10->shaders[i]); + + Release(d3d10->menu_pipeline_vbo); + Release(d3d10->blend_pipeline); + + Release(d3d10->ubo); + + Release(d3d10->blend_enable); + Release(d3d10->blend_disable); + + for (i = 0; i < RARCH_WRAP_MAX; i++) + { + Release(d3d10->samplers[RARCH_FILTER_LINEAR][i]); + Release(d3d10->samplers[RARCH_FILTER_NEAREST][i]); + } + + Release(d3d10->state); + Release(d3d10->renderTargetView); + Release(d3d10->swapChain); + + font_driver_free_osd(); + +#if 0 + if (video_driver_is_video_cache_context()) + { + cached_device_d3d10 = d3d10->device; + cached_context = d3d10->context; + } + else +#endif + { + Release(d3d10->device); + } + + win32_monitor_from_window(); + win32_destroy_window(); + free(d3d10); +} + static void* d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** input_data) { + unsigned i; MONITORINFOEX current_mon; HMONITOR hm_to_use; WNDCLASSEX wndclass = { 0 }; @@ -591,34 +587,31 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i if (!d3d10->vp.full_height) d3d10->vp.full_height = current_mon.rcMonitor.bottom - current_mon.rcMonitor.top; - if (!win32_set_video_mode(d3d10, video->width, video->height, video->fullscreen)) + if (!win32_set_video_mode(d3d10, d3d10->vp.full_width, d3d10->vp.full_height, video->fullscreen)) { RARCH_ERR("[D3D10]: win32_set_video_mode failed.\n"); goto error; } - dxgi_input_driver(settings->arrays.input_joypad_driver, input, input_data); { UINT flags = 0; DXGI_SWAP_CHAIN_DESC desc = {0}; - desc.BufferCount = 2; - desc.BufferDesc.Width = video->width; - desc.BufferDesc.Height = video->height; + desc.BufferCount = 1; + desc.BufferDesc.Width = d3d10->vp.full_width; + desc.BufferDesc.Height = d3d10->vp.full_height; desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; -#if 0 desc.BufferDesc.RefreshRate.Numerator = 60; desc.BufferDesc.RefreshRate.Denominator = 1; -#endif desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.OutputWindow = main_window.hwnd; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Windowed = TRUE; - desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; #if 0 - desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; + desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; #endif @@ -641,16 +634,17 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i Release(backBuffer); } - D3D10SetRenderTargets(d3d10->device, 1, &d3d10->renderTargetView, NULL); - d3d10->viewport.Width = video->width; - d3d10->viewport.Height = video->height; + D3D10SetRenderTargets(d3d10->device, 1, &d3d10->renderTargetView, NULL); + + video_driver_set_size(&d3d10->vp.full_width, &d3d10->vp.full_height); + d3d10->viewport.Width = d3d10->vp.full_width; + d3d10->viewport.Height = d3d10->vp.full_height; d3d10->resize_viewport = true; - d3d10->keep_aspect = video->force_aspect; d3d10->vsync = video->vsync; d3d10->format = video->rgb32 ? DXGI_FORMAT_B8G8R8X8_UNORM : DXGI_FORMAT_B5G6R5_UNORM; - d3d10->frame.texture[0].desc.Format = d3d10_get_closest_match_texture2D(d3d10->device, d3d10->format); + d3d10->frame.texture[0].desc.Format = d3d10->format; d3d10->frame.texture[0].desc.Usage = D3D10_USAGE_DEFAULT; d3d10->frame.texture[0].desc.Width = 4; d3d10->frame.texture[0].desc.Height = 4; @@ -659,7 +653,7 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i d3d10->menu.texture.desc.Usage = D3D10_USAGE_DEFAULT; - matrix_4x4_ortho(d3d10->mvp_no_rot, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); + matrix_4x4_ortho(d3d10->ubo_values.mvp, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); d3d10->ubo_values.OutputSize.width = d3d10->viewport.Width; d3d10->ubo_values.OutputSize.height = d3d10->viewport.Height; @@ -668,13 +662,13 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i D3D10_SUBRESOURCE_DATA ubo_data; D3D10_BUFFER_DESC desc; - desc.ByteWidth = sizeof(math_matrix_4x4); + desc.ByteWidth = sizeof(d3d10->ubo_values); desc.Usage = D3D10_USAGE_DYNAMIC; desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; desc.MiscFlags = 0; - ubo_data.pSysMem = &d3d10->mvp_no_rot; + ubo_data.pSysMem = &d3d10->ubo_values.mvp; ubo_data.SysMemPitch = 0; ubo_data.SysMemSlicePitch = 0; @@ -685,22 +679,11 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i d3d10_gfx_set_rotation(d3d10, 0); { - unsigned i; - unsigned k; - D3D10_SAMPLER_DESC desc; - - desc.Filter = D3D10_FILTER_MIN_MAG_MIP_POINT; - desc.AddressU = D3D10_TEXTURE_ADDRESS_BORDER; - desc.AddressV = D3D10_TEXTURE_ADDRESS_BORDER; - desc.AddressW = D3D10_TEXTURE_ADDRESS_BORDER; - desc.MipLODBias = 0.0f; - desc.MaxAnisotropy = 1; - desc.ComparisonFunc = D3D10_COMPARISON_NEVER; - desc.MinLOD = -D3D10_FLOAT32_MAX; - desc.MaxLOD = D3D10_FLOAT32_MAX; - - for (k = 0; k < 4; k++) - desc.BorderColor[k] = 0.0f; + D3D10_SAMPLER_DESC desc = { D3D10_FILTER_MIN_MAG_MIP_POINT }; + desc.MaxAnisotropy = 1; + desc.ComparisonFunc = D3D10_COMPARISON_NEVER; + desc.MinLOD = -D3D10_FLOAT32_MAX; + desc.MaxLOD = D3D10_FLOAT32_MAX; /* Initialize samplers */ for (i = 0; i < RARCH_WRAP_MAX; i++) @@ -882,27 +865,26 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i } { - unsigned k; - D3D10_BLEND_DESC blend_desc; - - for (k = 0; k < 8; k++) - { - blend_desc.BlendEnable[k] = TRUE; - blend_desc.RenderTargetWriteMask[k] = D3D10_COLOR_WRITE_ENABLE_ALL; - } - - blend_desc.AlphaToCoverageEnable = FALSE; - blend_desc.SrcBlend = D3D10_BLEND_SRC_ALPHA; - blend_desc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA; - blend_desc.BlendOp = D3D10_BLEND_OP_ADD; - blend_desc.SrcBlendAlpha = D3D10_BLEND_SRC_ALPHA; - blend_desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - blend_desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + D3D10_BLEND_DESC blend_desc = { 0 }; + blend_desc.AlphaToCoverageEnable = FALSE; + blend_desc.BlendEnable[0] = TRUE; + blend_desc.SrcBlend = D3D10_BLEND_SRC_ALPHA; + blend_desc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA; + blend_desc.BlendOp = D3D10_BLEND_OP_ADD; + blend_desc.SrcBlendAlpha = D3D10_BLEND_SRC_ALPHA; + blend_desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + blend_desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + blend_desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; D3D10CreateBlendState(d3d10->device, &blend_desc, &d3d10->blend_enable); + + blend_desc.SrcBlend = D3D10_BLEND_ONE; + blend_desc.DestBlend = D3D10_BLEND_ONE; + D3D10CreateBlendState(d3d10->device, &blend_desc, &d3d10->blend_pipeline); + blend_desc.BlendEnable[0] = FALSE; D3D10CreateBlendState(d3d10->device, &blend_desc, &d3d10->blend_disable); - } + } { D3D10_RASTERIZER_DESC desc = { (D3D10_FILL_MODE)0 }; @@ -930,8 +912,8 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i video_driver_get_hw_context()->version_major == 11) { d3d10->hw.enable = true; - d3d10->hw.iface.interface_type = RETRO_HW_RENDER_INTERFACE_D3D11; - d3d10->hw.iface.interface_version = RETRO_HW_RENDER_INTERFACE_D3D11_VERSION; + d3d10->hw.iface.interface_type = RETRO_HW_RENDER_INTERFACE_D3D10; + d3d10->hw.iface.interface_version = RETRO_HW_RENDER_INTERFACE_D3D10_VERSION; d3d10->hw.iface.handle = d3d10; d3d10->hw.iface.device = d3d10->device; d3d10->hw.iface.context = d3d10->context; @@ -1221,14 +1203,14 @@ static bool d3d10_gfx_frame( if (buffer_sem->stage_mask && buffer_sem->uniforms) { - D3D10_MAPPED_SUBRESOURCE res; + void* data; uniform_sem_t* uniform = buffer_sem->uniforms; - D3D10MapBuffer(buffer, D3D10_MAP_WRITE_DISCARD, 0, (void**)&res); + D3D10MapBuffer(buffer, D3D10_MAP_WRITE_DISCARD, 0, (void**)&data); while (uniform->size) { if (uniform->data) - memcpy((uint8_t*)res.pData + uniform->offset, uniform->data, uniform->size); + memcpy((uint8_t*)data + uniform->offset, uniform->data, uniform->size); uniform++; } D3D10UnmapBuffer(buffer); @@ -1392,6 +1374,10 @@ static bool d3d10_gfx_frame( static void d3d10_gfx_set_nonblock_state(void* data, bool toggle) { d3d10_video_t* d3d10 = (d3d10_video_t*)data; + + if (!d3d10) + return; + d3d10->vsync = !toggle; } @@ -1451,14 +1437,13 @@ static bool d3d10_gfx_read_viewport(void* data, uint8_t* buffer, bool is_idle) static void d3d10_set_menu_texture_frame( void* data, const void* frame, bool rgb32, unsigned width, unsigned height, float alpha) { - d3d10_video_t* d3d10 = (d3d10_video_t*)data; - settings_t* settings = config_get_ptr(); - int pitch = width * (rgb32 ? sizeof(uint32_t) - : sizeof(uint16_t)); - DXGI_FORMAT format = rgb32 ? DXGI_FORMAT_B8G8R8A8_UNORM - : (DXGI_FORMAT)DXGI_FORMAT_EX_A4R4G4B4_UNORM; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; + settings_t* settings = config_get_ptr(); + DXGI_FORMAT format = rgb32 ? DXGI_FORMAT_B8G8R8A8_UNORM : + (DXGI_FORMAT)DXGI_FORMAT_EX_A4R4G4B4_UNORM; - if ( d3d10->menu.texture.desc.Width != width || + if ( + d3d10->menu.texture.desc.Width != width || d3d10->menu.texture.desc.Height != height) { d3d10->menu.texture.desc.Format = format; @@ -1467,19 +1452,20 @@ static void d3d10_set_menu_texture_frame( d3d10_init_texture(d3d10->device, &d3d10->menu.texture); } - d3d10_update_texture(d3d10->device, - width, height, pitch, format, - frame, &d3d10->menu.texture); + d3d10_update_texture(d3d10->device, width, height, 0, + format, frame, &d3d10->menu.texture); d3d10->menu.texture.sampler = d3d10->samplers [settings->bools.menu_linear_filter ? RARCH_FILTER_LINEAR : RARCH_FILTER_NEAREST][RARCH_WRAP_DEFAULT]; } - static void d3d10_set_menu_texture_enable(void* data, bool state, bool full_screen) { d3d10_video_t* d3d10 = (d3d10_video_t*)data; + if (!d3d10) + return; + d3d10->menu.enabled = state; d3d10->menu.fullscreen = full_screen; } @@ -1609,7 +1595,7 @@ static const video_poke_interface_t d3d10_poke_interface = { d3d10_gfx_get_current_shader, NULL, /* get_current_software_framebuffer */ #if 0 - d3d11_get_hw_render_interface, + d3d10_get_hw_render_interface, #else NULL, /* get_hw_render_interface */ #endif diff --git a/gfx/drivers_font/d3d10_font.c b/gfx/drivers_font/d3d10_font.c index 43a0c309c3..3252854303 100644 --- a/gfx/drivers_font/d3d10_font.c +++ b/gfx/drivers_font/d3d10_font.c @@ -130,7 +130,7 @@ static void d3d10_font_render_line( unsigned text_align) { unsigned i, count; - D3D10_MAPPED_SUBRESOURCE mapped_vbo; + void* mapped_vbo; d3d10_sprite_t* v; d3d10_video_t* d3d10 = (d3d10_video_t*)video_info->userdata; unsigned width = video_info->width; @@ -158,7 +158,7 @@ static void d3d10_font_render_line( } D3D10MapBuffer(d3d10->sprites.vbo, D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); - v = (d3d10_sprite_t*)mapped_vbo.pData + d3d10->sprites.offset; + v = (d3d10_sprite_t*)mapped_vbo + d3d10->sprites.offset; for (i = 0; i < msg_len; i++) { @@ -202,7 +202,7 @@ static void d3d10_font_render_line( y += glyph->advance_y * scale; } - count = v - ((d3d10_sprite_t*)mapped_vbo.pData + d3d10->sprites.offset); + count = v - ((d3d10_sprite_t*)mapped_vbo + d3d10->sprites.offset); D3D10UnmapBuffer(d3d10->sprites.vbo); if (!count) diff --git a/menu/drivers_display/menu_display_d3d10.c b/menu/drivers_display/menu_display_d3d10.c index d5e200a638..7579fc8791 100644 --- a/menu/drivers_display/menu_display_d3d10.c +++ b/menu/drivers_display/menu_display_d3d10.c @@ -101,12 +101,12 @@ static void menu_display_d3d10_draw(void* data, video_frame_info_t *video_info) d3d10->sprites.offset = 0; { - D3D10_MAPPED_SUBRESOURCE mapped_vbo; - d3d10_sprite_t* sprite = NULL; + void* mapped_vbo; + d3d10_sprite_t* sprite = NULL; - D3D10MapBuffer(d3d10->sprites.vbo, D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&mapped_vbo); + D3D10MapBuffer(d3d10->sprites.vbo, D3D10_MAP_WRITE_NO_OVERWRITE, 0, &mapped_vbo); - sprite = (d3d10_sprite_t*)mapped_vbo.pData + d3d10->sprites.offset; + sprite = (d3d10_sprite_t*)mapped_vbo + d3d10->sprites.offset; if (vertex_count == 1) { @@ -231,9 +231,9 @@ static void menu_display_d3d10_draw_pipeline(void* data, d3d10->ubo_values.time += 0.01f; { - D3D10_MAPPED_SUBRESOURCE mapped_ubo; + void* mapped_ubo; D3D10MapBuffer(d3d10->ubo, D3D10_MAP_WRITE_DISCARD, 0, (void**)&mapped_ubo); - *(d3d10_uniform_t*)mapped_ubo.pData = d3d10->ubo_values; + *(d3d10_uniform_t*)mapped_ubo = d3d10->ubo_values; D3D10UnmapBuffer(d3d10->ubo); } } From 8848b2837ba9c2feb5e128d7471a854d31566294 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 05:11:24 +0200 Subject: [PATCH 341/517] Add D3D10 to slang supported checks --- gfx/video_shader_parse.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gfx/video_shader_parse.c b/gfx/video_shader_parse.c index 38f0655827..ed78aa8189 100644 --- a/gfx/video_shader_parse.c +++ b/gfx/video_shader_parse.c @@ -1215,6 +1215,7 @@ enum rarch_shader_type video_shader_get_type_from_ext( { switch (api) { + case GFX_CTX_DIRECT3D10_API: case GFX_CTX_DIRECT3D11_API: case GFX_CTX_DIRECT3D12_API: case GFX_CTX_GX2_API: @@ -1232,6 +1233,7 @@ enum rarch_shader_type video_shader_get_type_from_ext( switch (api) { + case GFX_CTX_DIRECT3D10_API: case GFX_CTX_DIRECT3D11_API: case GFX_CTX_DIRECT3D12_API: case GFX_CTX_GX2_API: From e0b57843aa8b5ab30c85c191b9951e3fcf827f31 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 05:14:50 +0200 Subject: [PATCH 342/517] Enable HAVE_D3D10 now for main Windows build --- qb/config.params.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qb/config.params.sh b/qb/config.params.sh index f3899a3403..6b2aec7ffb 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -38,7 +38,7 @@ C89_BUILTINMINIUPNPC=no HAVE_D3D8=no # Direct3D 8 support HAVE_D3D9=yes # Direct3D 9 support C89_D3D9=no -HAVE_D3D10=no # Direct3D 10 support +HAVE_D3D10=yes # Direct3D 10 support C89_D3D10=no HAVE_D3D11=yes # Direct3D 11 support C89_D3D11=no From 832cc4e945e6ab0c09c94ee6e8b1ee3c98ee1042 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 05:20:22 +0200 Subject: [PATCH 343/517] (MSVC) Silence warnings --- gfx/common/d3d12_common.c | 2 +- gfx/drivers/d3d10.c | 12 ++++++------ gfx/drivers/d3d11.c | 12 ++++++------ gfx/drivers/d3d12.c | 10 +++++----- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/gfx/common/d3d12_common.c b/gfx/common/d3d12_common.c index 169e1d6ce4..1b8d9478c5 100644 --- a/gfx/common/d3d12_common.c +++ b/gfx/common/d3d12_common.c @@ -273,7 +273,7 @@ static D3D12_CPU_DESCRIPTOR_HANDLE d3d12_descriptor_heap_slot_alloc(d3d12_descri int i; D3D12_CPU_DESCRIPTOR_HANDLE handle = { 0 }; - for (i = heap->start; i < heap->desc.NumDescriptors; i++) + for (i = heap->start; i < (int)heap->desc.NumDescriptors; i++) { if (!heap->map[i]) { diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index c7e3e7890c..900b8c539c 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -1,4 +1,4 @@ -/* RetroArch - A frontend for libretro. +/* RetroArch - A frontend for libretro. * Copyright (C) 2014-2018 - Ali Bouhlel * * RetroArch is free software: you can redistribute it and/or modify it under the terms @@ -37,7 +37,7 @@ static void d3d10_free_overlays(d3d10_video_t* d3d10) { unsigned i; - for (i = 0; i < d3d10->overlays.count; i++) + for (i = 0; i < (unsigned)d3d10->overlays.count; i++) d3d10_release_texture(&d3d10->overlays.textures[i]); Release(d3d10->overlays.vbo); @@ -126,7 +126,7 @@ static bool d3d10_overlay_load(void* data, const void* image_data, unsigned num_ D3D10MapBuffer(d3d10->overlays.vbo, D3D10_MAP_WRITE_DISCARD, 0, (void**)&sprites); - for (i = 0; i < num_images; i++) + for (i = 0; i < (unsigned)num_images; i++) { d3d10->overlays.textures[i].desc.Width = images[i].width; @@ -280,7 +280,7 @@ static void d3d10_free_shader_preset(d3d10_video_t* d3d10) memset(d3d10->pass, 0, sizeof(d3d10->pass)); /* only free the history textures here */ - for (i = 1; i <= d3d10->shader_preset->history_size; i++) + for (i = 1; i <= (unsigned)d3d10->shader_preset->history_size; i++) d3d10_release_texture(&d3d10->frame.texture[i]); memset( @@ -938,7 +938,7 @@ static void d3d10_init_history(d3d10_video_t* d3d10, unsigned width, unsigned he * and to reduce memory fragmentation */ assert(d3d10->shader_preset); - for (i = 0; i < d3d10->shader_preset->history_size + 1; i++) + for (i = 0; i < (unsigned)d3d10->shader_preset->history_size + 1; i++) { d3d10->frame.texture[i].desc.Width = width; d3d10->frame.texture[i].desc.Height = height; @@ -1347,7 +1347,7 @@ static bool d3d10_gfx_frame( D3D10SetPShaderSamplers( context, 0, 1, &d3d10->samplers[RARCH_FILTER_UNSPEC][RARCH_WRAP_DEFAULT]); - for (i = 0; i < d3d10->overlays.count; i++) + for (i = 0; i < (unsigned)d3d10->overlays.count; i++) { D3D10SetPShaderResources(context, 0, 1, &d3d10->overlays.textures[i].view); D3D10Draw(d3d10->device, 1, i); diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 839b8c7608..7e03b71bcb 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -1,4 +1,4 @@ -/* RetroArch - A frontend for libretro. +/* RetroArch - A frontend for libretro. * Copyright (C) 2014-2018 - Ali Bouhlel * * RetroArch is free software: you can redistribute it and/or modify it under the terms @@ -49,7 +49,7 @@ static D3D11DeviceContext cached_context; static void d3d11_free_overlays(d3d11_video_t* d3d11) { unsigned i; - for (i = 0; i < d3d11->overlays.count; i++) + for (i = 0; i < (unsigned)d3d11->overlays.count; i++) d3d11_release_texture(&d3d11->overlays.textures[i]); Release(d3d11->overlays.vbo); @@ -120,7 +120,7 @@ static bool d3d11_overlay_load(void* data, const void* image_data, unsigned num_ { D3D11_BUFFER_DESC desc; D3D11_MAPPED_SUBRESOURCE mapped_vbo; - int i; + unsigned i; d3d11_sprite_t* sprites; d3d11_video_t* d3d11 = (d3d11_video_t*)data; const struct texture_image* images = (const struct texture_image*)image_data; @@ -296,7 +296,7 @@ static void d3d11_free_shader_preset(d3d11_video_t* d3d11) memset(d3d11->pass, 0, sizeof(d3d11->pass)); /* only free the history textures here */ - for (i = 1; i <= d3d11->shader_preset->history_size; i++) + for (i = 1; i <= (unsigned)d3d11->shader_preset->history_size; i++) d3d11_release_texture(&d3d11->frame.texture[i]); memset( @@ -985,7 +985,7 @@ static void d3d11_init_history(d3d11_video_t* d3d11, unsigned width, unsigned he * and to reduce memory fragmentation */ assert(d3d11->shader_preset); - for (i = 0; i < d3d11->shader_preset->history_size + 1; i++) + for (i = 0; i < (unsigned)d3d11->shader_preset->history_size + 1; i++) { d3d11->frame.texture[i].desc.Width = width; d3d11->frame.texture[i].desc.Height = height; @@ -1378,7 +1378,7 @@ static bool d3d11_gfx_frame( D3D11SetPShaderSamplers( context, 0, 1, &d3d11->samplers[RARCH_FILTER_UNSPEC][RARCH_WRAP_DEFAULT]); - for (i = 0; i < d3d11->overlays.count; i++) + for (i = 0; i < (unsigned)d3d11->overlays.count; i++) { D3D11SetPShaderResources(context, 0, 1, &d3d11->overlays.textures[i].view); D3D11Draw(d3d11->context, 1, i); diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 508cab852c..160232e818 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -48,7 +48,7 @@ static void d3d12_gfx_sync(d3d12_video_t* d3d12) static void d3d12_free_overlays(d3d12_video_t* d3d12) { unsigned i; - for (i = 0; i < d3d12->overlays.count; i++) + for (i = 0; i < (unsigned)d3d12->overlays.count; i++) d3d12_release_texture(&d3d12->overlays.textures[i]); Release(d3d12->overlays.vbo); @@ -120,7 +120,7 @@ static void d3d12_overlay_set_alpha(void* data, unsigned index, float mod) static bool d3d12_overlay_load(void* data, const void* image_data, unsigned num_images) { - int i; + unsigned i; d3d12_sprite_t* sprites = NULL; D3D12_RANGE range = { 0, 0 }; d3d12_video_t* d3d12 = (d3d12_video_t*)data; @@ -304,7 +304,7 @@ static void d3d12_free_shader_preset(d3d12_video_t* d3d12) memset(d3d12->pass, 0, sizeof(d3d12->pass)); /* only free the history textures here */ - for (i = 1; i <= d3d12->shader_preset->history_size; i++) + for (i = 1; i <= (unsigned)d3d12->shader_preset->history_size; i++) d3d12_release_texture(&d3d12->frame.texture[i]); memset( @@ -993,7 +993,7 @@ static void d3d12_init_history(d3d12_video_t* d3d12, unsigned width, unsigned he * and to reduce memory fragmentation */ assert(d3d12->shader_preset); - for (i = 0; i < d3d12->shader_preset->history_size + 1; i++) + for (i = 0; i < (unsigned)d3d12->shader_preset->history_size + 1; i++) { d3d12->frame.texture[i].desc.Width = width; d3d12->frame.texture[i].desc.Height = height; @@ -1492,7 +1492,7 @@ static bool d3d12_gfx_frame( d3d12->queue.cmd, ROOT_ID_SAMPLER_T, d3d12->samplers[RARCH_FILTER_UNSPEC][RARCH_WRAP_DEFAULT]); - for (i = 0; i < d3d12->overlays.count; i++) + for (i = 0; i < (unsigned)d3d12->overlays.count; i++) { if (d3d12->overlays.textures[i].dirty) d3d12_upload_texture(d3d12->queue.cmd, From 6929db9fb6bdbe741b33e9cfbd3db1399cd92ac6 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 05:36:59 +0200 Subject: [PATCH 344/517] (D3D10/D3D11) Compile shaders with SM 4.0 instead of 5.0 --- gfx/common/d3d11_common.c | 12 ++++++------ gfx/drivers/d3d10.c | 2 +- gfx/drivers/d3d11.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gfx/common/d3d11_common.c b/gfx/common/d3d11_common.c index d236f9bcfa..16b32151e1 100644 --- a/gfx/common/d3d11_common.c +++ b/gfx/common/d3d11_common.c @@ -205,20 +205,20 @@ bool d3d11_init_shader( if (!src) /* LPCWSTR filename */ { - if (vs_entry && !d3d_compile_from_file((LPCWSTR)src_name, vs_entry, "vs_5_0", &vs_code)) + if (vs_entry && !d3d_compile_from_file((LPCWSTR)src_name, vs_entry, "vs_4_0", &vs_code)) success = false; - if (ps_entry && !d3d_compile_from_file((LPCWSTR)src_name, ps_entry, "ps_5_0", &ps_code)) + if (ps_entry && !d3d_compile_from_file((LPCWSTR)src_name, ps_entry, "ps_4_0", &ps_code)) success = false; - if (gs_entry && !d3d_compile_from_file((LPCWSTR)src_name, gs_entry, "gs_5_0", &gs_code)) + if (gs_entry && !d3d_compile_from_file((LPCWSTR)src_name, gs_entry, "gs_4_0", &gs_code)) success = false; } else /* char array */ { - if (vs_entry && !d3d_compile(src, size, (LPCSTR)src_name, vs_entry, "vs_5_0", &vs_code)) + if (vs_entry && !d3d_compile(src, size, (LPCSTR)src_name, vs_entry, "vs_4_0", &vs_code)) success = false; - if (ps_entry && !d3d_compile(src, size, (LPCSTR)src_name, ps_entry, "ps_5_0", &ps_code)) + if (ps_entry && !d3d_compile(src, size, (LPCSTR)src_name, ps_entry, "ps_4_0", &ps_code)) success = false; - if (gs_entry && !d3d_compile(src, size, (LPCSTR)src_name, gs_entry, "gs_5_0", &gs_code)) + if (gs_entry && !d3d_compile(src, size, (LPCSTR)src_name, gs_entry, "gs_4_0", &gs_code)) success = false; } diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 900b8c539c..eb9ecd3026 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -374,7 +374,7 @@ static bool d3d10_gfx_set_shader(void* data, /* clang-format on */ if (!slang_process( - d3d10->shader_preset, i, RARCH_SHADER_HLSL, 50, &semantics_map, + d3d10->shader_preset, i, RARCH_SHADER_HLSL, 40, &semantics_map, &d3d10->pass[i].semantics)) goto error; diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 7e03b71bcb..bdac1dca9f 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -389,7 +389,7 @@ static bool d3d11_gfx_set_shader(void* data, enum rarch_shader_type type, const /* clang-format on */ if (!slang_process( - d3d11->shader_preset, i, RARCH_SHADER_HLSL, 50, &semantics_map, + d3d11->shader_preset, i, RARCH_SHADER_HLSL, 40, &semantics_map, &d3d11->pass[i].semantics)) goto error; From e5d90eebd76f46d34b6fe376ebba401cfb4553d5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 06:29:56 +0200 Subject: [PATCH 345/517] Updates to libretro-common --- libretro-common/audio/dsp_filters/Tremolo.dsp | 7 + libretro-common/audio/dsp_filters/Vibrato.dsp | 7 + libretro-common/audio/dsp_filters/tremolo.c | 133 ++++++++++++++ libretro-common/audio/dsp_filters/vibrato.c | 169 ++++++++++++++++++ libretro-common/compat/compat_vscprintf.c | 44 +++++ libretro-common/formats/libchdr/chd.c | 4 +- 6 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 libretro-common/audio/dsp_filters/Tremolo.dsp create mode 100644 libretro-common/audio/dsp_filters/Vibrato.dsp create mode 100644 libretro-common/audio/dsp_filters/tremolo.c create mode 100644 libretro-common/audio/dsp_filters/vibrato.c create mode 100644 libretro-common/compat/compat_vscprintf.c diff --git a/libretro-common/audio/dsp_filters/Tremolo.dsp b/libretro-common/audio/dsp_filters/Tremolo.dsp new file mode 100644 index 0000000000..50523333ac --- /dev/null +++ b/libretro-common/audio/dsp_filters/Tremolo.dsp @@ -0,0 +1,7 @@ +filters = 1 +filter0 = tremolo + +# Defaults. +#tremolo_frequency = 4.0 +#tremolo_depth = 0.9 + diff --git a/libretro-common/audio/dsp_filters/Vibrato.dsp b/libretro-common/audio/dsp_filters/Vibrato.dsp new file mode 100644 index 0000000000..b1ea8e1107 --- /dev/null +++ b/libretro-common/audio/dsp_filters/Vibrato.dsp @@ -0,0 +1,7 @@ +filters = 1 +filter0 = vibrato + +# Defaults. +#vibrato_frequency = 5.0 +#vibrato_depth = 0.5 + diff --git a/libretro-common/audio/dsp_filters/tremolo.c b/libretro-common/audio/dsp_filters/tremolo.c new file mode 100644 index 0000000000..38b535742c --- /dev/null +++ b/libretro-common/audio/dsp_filters/tremolo.c @@ -0,0 +1,133 @@ +/* Copyright (C) 2010-2017 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (tremolo.c). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + +#include +#include +#include + +#include +#include +#include + +#define sqr(a) ((a) * (a)) + +struct tremolo_core +{ + float freq; + float depth; + float* wavetable; + int index; + int maxindex; +}; + +struct tremolo +{ + struct tremolo_core left, right; +}; + +static void tremolo_free(void *data) +{ + struct tremolo *tre = (struct tremolo*)data; + free(tre->left.wavetable); + free(tre->right.wavetable); + free(data); +} + +static void tremolocore_init(struct tremolo_core *core,float depth,int samplerate,float freq) +{ + const double offset = 1. - depth / 2.; + unsigned i; + double env; + core->index = 0; + core->maxindex = samplerate/freq; + core->wavetable = malloc(core->maxindex*sizeof(float)); + memset(core->wavetable, 0, core->maxindex * sizeof(float)); + for (i = 0; i < core->maxindex; i++) { + env = freq * i / samplerate; + env = sin((M_PI*2) * fmod(env + 0.25, 1.0)); + core->wavetable[i] = env * (1 - fabs(offset)) + offset; + } +} + +float tremolocore_core(struct tremolo_core *core,float in) +{ + core->index = core->index % core->maxindex; + return in * core->wavetable[core->index++]; +} + +static void tremolo_process(void *data, struct dspfilter_output *output, + const struct dspfilter_input *input) +{ + unsigned i; + float *out; + struct tremolo *tre = (struct tremolo*)data; + + output->samples = input->samples; + output->frames = input->frames; + out = output->samples; + + for (i = 0; i < input->frames; i++, out += 2) + { + float in[2] = { out[0], out[1] }; + + out[0] = tremolocore_core(&tre->left, in[0]); + out[1] = tremolocore_core(&tre->right, in[1]); + } +} + +static void *tremolo_init(const struct dspfilter_info *info, + const struct dspfilter_config *config, void *userdata) +{ + float freq, depth; + struct tremolo *tre = (struct tremolo*)calloc(1, sizeof(*tre)); + if (!tre) + return NULL; + + config->get_float(userdata, "freq", &freq,4.0f); + config->get_float(userdata, "depth", &depth, 0.9f); + tremolocore_init(&tre->left,depth,info->input_rate,freq); + tremolocore_init(&tre->right,depth,info->input_rate,freq); + return tre; +} + +static const struct dspfilter_implementation tremolo_plug = { + tremolo_init, + tremolo_process, + tremolo_free, + + DSPFILTER_API_VERSION, + "Tremolo", + "tremolo", +}; + +#ifdef HAVE_FILTERS_BUILTIN +#define dspfilter_get_implementation tremolo_dspfilter_get_implementation +#endif + +const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) +{ + (void)mask; + return &tremolo_plug; +} + +#undef dspfilter_get_implementation + diff --git a/libretro-common/audio/dsp_filters/vibrato.c b/libretro-common/audio/dsp_filters/vibrato.c new file mode 100644 index 0000000000..6e79d2f0fb --- /dev/null +++ b/libretro-common/audio/dsp_filters/vibrato.c @@ -0,0 +1,169 @@ +/* Copyright (C) 2010-2017 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (vibrato.c). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + +#include +#include +#include + +#include +#include +#include + +#define sqr(a) ((a) * (a)) + + +const float BASE_DELAY_SEC = 0.002; // 2 ms +const float VIBRATO_FREQUENCY_DEFAULT_HZ = 2; +const float VIBRATO_FREQUENCY_MAX_HZ = 14; +const float VIBRATO_DEPTH_DEFAULT_PERCENT = 50; +const int add_delay = 3; + +float hermite_interp(float x, float *y) +{ + float c0, c1, c2, c3; + c0 = y[1]; + c1 = (1.0 / 2.0)*(y[2] - y[0]); + c2 = (y[0] - (5.0 / 2.0)*y[1]) + (2.0*y[2] - (1.0 / 2.0)*y[3]); + c3 = (1.0 / 2.0)*(y[3] - y[0]) + (3.0 / 2.0)*(y[1] - y[2]); + return ((c3*x + c2)*x + c1)*x + c0; +} + +struct vibrato_core +{ + float freq; + float samplerate; + int phase; + float depth; + float* buffer; + int writeindex; + int size; +}; + +struct vibrato +{ + struct vibrato_core left, right; +}; + +static void vibrato_free(void *data) +{ + struct vibrato *vib = (struct vibrato*)data; + free(vib->left.buffer); + free(vib->right.buffer); + free(data); +} + +static void vibratocore_init(struct vibrato_core *core,float depth,int samplerate,float freq) +{ + core->size = BASE_DELAY_SEC * samplerate * 2; + core->buffer = malloc((core->size + add_delay)*sizeof(float)); + memset(core->buffer, 0, (core->size + add_delay) * sizeof(float)); + core->samplerate = samplerate; + core->freq = freq; + core->depth = depth; + core->phase = 0; + core->writeindex = 0; +} + +float vibratocore_core(struct vibrato_core *core,float in) +{ + float M = core->freq / core->samplerate; + int maxphase = core->samplerate / core->freq; + float lfo = sin(M * 2. * M_PI * core->phase++); + core->phase = core->phase % maxphase; + lfo = (lfo + 1) * 1.; // transform from [-1; 1] to [0; 1] + int maxdelay = BASE_DELAY_SEC * core->samplerate; + float delay = lfo * core->depth * maxdelay; + delay += add_delay; + float readindex = core->writeindex - 1 - delay; + while (readindex < 0)readindex += core->size; + while (readindex >= core->size)readindex -= core->size; + int ipart = (int)readindex; // integer part of the delay + float fpart = readindex - ipart; // fractional part of the delay + float value = hermite_interp(fpart, &(core->buffer[ipart])); + core->buffer[core->writeindex] = in; + if (core->writeindex < add_delay){ + core->buffer[core->size + core->writeindex] = in; + } + core->writeindex++; + if (core->writeindex == core->size) { + core->writeindex = 0; + } + return value; +} + +static void vibrato_process(void *data, struct dspfilter_output *output, + const struct dspfilter_input *input) +{ + unsigned i; + float *out; + struct vibrato *vib = (struct vibrato*)data; + + output->samples = input->samples; + output->frames = input->frames; + out = output->samples; + + for (i = 0; i < input->frames; i++, out += 2) + { + float in[2] = { out[0], out[1] }; + + out[0] = vibratocore_core(&vib->left, in[0]); + out[1] = vibratocore_core(&vib->right, in[1]); + } +} + +static void *vibrato_init(const struct dspfilter_info *info, + const struct dspfilter_config *config, void *userdata) +{ + float freq, depth; + struct vibrato *vib = (struct vibrato*)calloc(1, sizeof(*vib)); + if (!vib) + return NULL; + + config->get_float(userdata, "freq", &freq,5.0f); + config->get_float(userdata, "depth", &depth, 0.5f); + vibratocore_init(&vib->left,depth,info->input_rate,freq); + vibratocore_init(&vib->right,depth,info->input_rate,freq); + return vib; +} + +static const struct dspfilter_implementation vibrato_plug = { + vibrato_init, + vibrato_process, + vibrato_free, + + DSPFILTER_API_VERSION, + "Vibrato", + "vibrato", +}; + +#ifdef HAVE_FILTERS_BUILTIN +#define dspfilter_get_implementation vibrato_dspfilter_get_implementation +#endif + +const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) +{ + (void)mask; + return &vibrato_plug; +} + +#undef dspfilter_get_implementation + diff --git a/libretro-common/compat/compat_vscprintf.c b/libretro-common/compat/compat_vscprintf.c new file mode 100644 index 0000000000..ddffeb7e77 --- /dev/null +++ b/libretro-common/compat/compat_vscprintf.c @@ -0,0 +1,44 @@ +/* Copyright (C) 2010-2017 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (compat_snprintf.c). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + +/* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */ +#ifdef _MSC_VER + +#include + +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1800 +#define va_copy(dst, src) ((dst) = (src)) +#endif + +int c89_vscprintf_retro__(const char *format, va_list pargs) +{ + int retval; + va_list argcopy; + va_copy(argcopy, pargs); + retval = vsnprintf(NULL, 0, format, argcopy); + va_end(argcopy); + return retval; +} +#endif diff --git a/libretro-common/formats/libchdr/chd.c b/libretro-common/formats/libchdr/chd.c index bb8e91de11..32e9dd1045 100644 --- a/libretro-common/formats/libchdr/chd.c +++ b/libretro-common/formats/libchdr/chd.c @@ -1706,7 +1706,7 @@ chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, { metadata_entry metaentry; chd_error err; - int64_t count; + UINT32 count; /* if we didn't find it, just return */ err = metadata_find_entry(chd, searchtag, searchindex, &metaentry); @@ -2240,7 +2240,7 @@ static chd_error map_read(chd_file *chd) UINT8 raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; UINT64 fileoffset, maxoffset = 0; UINT8 cookie[MAP_ENTRY_SIZE]; - int64_t count; + UINT32 count; chd_error err; int i; From 88d62c776631d5b370fa36bfb933d50f244fc5f5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 06:31:07 +0200 Subject: [PATCH 346/517] Update --- libretro-common/formats/libchdr/chd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libretro-common/formats/libchdr/chd.c b/libretro-common/formats/libchdr/chd.c index 32e9dd1045..bb8e91de11 100644 --- a/libretro-common/formats/libchdr/chd.c +++ b/libretro-common/formats/libchdr/chd.c @@ -1706,7 +1706,7 @@ chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, { metadata_entry metaentry; chd_error err; - UINT32 count; + int64_t count; /* if we didn't find it, just return */ err = metadata_find_entry(chd, searchtag, searchindex, &metaentry); @@ -2240,7 +2240,7 @@ static chd_error map_read(chd_file *chd) UINT8 raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; UINT64 fileoffset, maxoffset = 0; UINT8 cookie[MAP_ENTRY_SIZE]; - UINT32 count; + int64_t count; chd_error err; int i; From df5586570f21bbcdc417fdc8fcec2a964c9a2da2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 08:42:57 +0200 Subject: [PATCH 347/517] Make gl2_renderchain_deinit_fbo safer --- CHANGES.md | 2 ++ gfx/drivers_renderchain/gl2_renderchain.c | 37 ++++++++++++----------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b538863ba3..c1e3c076e9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,8 @@ - CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. - CHEEVOS: Fixed incompatibilities with Neo Geo Pocket achievement sets. +- D3D10: Added D3D10 driver to release build. Has working shaders (Slang), overlay, and menu display driver support. Should be on par capabilities wise +with D3D11 driver except for there being no hardware rendering right now. - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. - D3D11/D3D12: Fix crashes with completely black or white thumbnail textures in XMB. - LIBRETRO: Addition - Functions to enable and disable audio and video, and an environment function to query status of audio and video enables. diff --git a/gfx/drivers_renderchain/gl2_renderchain.c b/gfx/drivers_renderchain/gl2_renderchain.c index 1cb856c4e8..6d4e7c0b18 100644 --- a/gfx/drivers_renderchain/gl2_renderchain.c +++ b/gfx/drivers_renderchain/gl2_renderchain.c @@ -504,27 +504,30 @@ static void gl2_renderchain_deinit_fbo(void *data, gl_t *gl = (gl_t*)data; gl2_renderchain_t *chain = (gl2_renderchain_t*)chain_data; - if (!gl) - return; + if (gl) + { + if (gl->fbo_feedback) + gl2_delete_fb(1, &gl->fbo_feedback); + if (gl->fbo_feedback_texture) + glDeleteTextures(1, &gl->fbo_feedback_texture); - glDeleteTextures(chain->fbo_pass, chain->fbo_texture); - gl2_delete_fb(chain->fbo_pass, chain->fbo); + gl->fbo_inited = false; + gl->fbo_feedback_enable = false; + gl->fbo_feedback_pass = 0; + gl->fbo_feedback_texture = 0; + gl->fbo_feedback = 0; + } - memset(chain->fbo_texture, 0, sizeof(chain->fbo_texture)); - memset(chain->fbo, 0, sizeof(chain->fbo)); + if (chain) + { + glDeleteTextures(chain->fbo_pass, chain->fbo_texture); + gl2_delete_fb(chain->fbo_pass, chain->fbo); - if (gl->fbo_feedback) - gl2_delete_fb(1, &gl->fbo_feedback); - if (gl->fbo_feedback_texture) - glDeleteTextures(1, &gl->fbo_feedback_texture); + memset(chain->fbo_texture, 0, sizeof(chain->fbo_texture)); + memset(chain->fbo, 0, sizeof(chain->fbo)); - chain->fbo_pass = 0; - - gl->fbo_inited = false; - gl->fbo_feedback_enable = false; - gl->fbo_feedback_pass = 0; - gl->fbo_feedback_texture = 0; - gl->fbo_feedback = 0; + chain->fbo_pass = 0; + } } static void gl2_renderchain_deinit_hw_render( From c52f53aada6d3aca9ba6711665d7e10001365389 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 08:47:09 +0200 Subject: [PATCH 348/517] Some minor refactors --- gfx/drivers_shader/slang_process.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/gfx/drivers_shader/slang_process.cpp b/gfx/drivers_shader/slang_process.cpp index c8bbf95761..f4bf258813 100644 --- a/gfx/drivers_shader/slang_process.cpp +++ b/gfx/drivers_shader/slang_process.cpp @@ -94,10 +94,12 @@ static bool slang_process_reflection( const semantics_map_t* map, pass_semantics_t* out) { + int semantic; + unsigned i; unordered_map texture_semantic_map; unordered_map texture_semantic_uniform_map; - for (unsigned i = 0; i <= pass_number; i++) + for (i = 0; i <= pass_number; i++) { if (!*shader_info->pass[i].alias) continue; @@ -125,7 +127,7 @@ static bool slang_process_reflection( return false; } - for (unsigned i = 0; i < shader_info->luts; i++) + for (i = 0; i < shader_info->luts; i++) { if (!set_unique_map( texture_semantic_map, shader_info->lut[i].id, @@ -140,7 +142,7 @@ static bool slang_process_reflection( unordered_map uniform_semantic_map; - for (unsigned i = 0; i < shader_info->num_parameters; i++) + for (i = 0; i < shader_info->num_parameters; i++) { if (!set_unique_map( uniform_semantic_map, shader_info->parameters[i].id, @@ -171,7 +173,7 @@ static bool slang_process_reflection( vector uniforms[SLANG_CBUFFER_MAX]; vector textures; - for (int semantic = 0; semantic < SLANG_NUM_BASE_SEMANTICS; semantic++) + for (semantic = 0; semantic < SLANG_NUM_BASE_SEMANTICS; semantic++) { slang_semantic_meta& src = sl_reflection.semantics[semantic]; if (src.push_constant || src.uniform) @@ -195,7 +197,7 @@ static bool slang_process_reflection( } } - for (int i = 0; i < sl_reflection.semantic_float_parameters.size(); i++) + for (i = 0; i < sl_reflection.semantic_float_parameters.size(); i++) { slang_semantic_meta& src = sl_reflection.semantic_float_parameters[i]; @@ -219,9 +221,11 @@ static bool slang_process_reflection( } } - for (int semantic = 0; semantic < SLANG_NUM_TEXTURE_SEMANTICS; semantic++) + for (semantic = 0; semantic < SLANG_NUM_TEXTURE_SEMANTICS; semantic++) { - for (int index = 0; index < sl_reflection.semantic_textures[semantic].size(); index++) + unsigned index; + + for (index = 0; index < sl_reflection.semantic_textures[semantic].size(); index++) { slang_texture_semantic_meta& src = sl_reflection.semantic_textures[semantic][index]; @@ -289,7 +293,7 @@ static bool slang_process_reflection( out->textures = (texture_sem_t*)malloc(textures.size() * sizeof(*textures.data())); memcpy(out->textures, textures.data(), textures.size() * sizeof(*textures.data())); - for (int i = 0; i < SLANG_CBUFFER_MAX; i++) + for (i = 0; i < SLANG_CBUFFER_MAX; i++) { if (uniforms[i].empty()) continue; From 6d7ffef3167edac9246fdb09e3083f3e1c195ba6 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 08:52:38 +0200 Subject: [PATCH 349/517] (gl2_renderchain.c) Update --- gfx/drivers_renderchain/gl2_renderchain.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gfx/drivers_renderchain/gl2_renderchain.c b/gfx/drivers_renderchain/gl2_renderchain.c index 6d4e7c0b18..eb819457de 100644 --- a/gfx/drivers_renderchain/gl2_renderchain.c +++ b/gfx/drivers_renderchain/gl2_renderchain.c @@ -520,8 +520,10 @@ static void gl2_renderchain_deinit_fbo(void *data, if (chain) { - glDeleteTextures(chain->fbo_pass, chain->fbo_texture); - gl2_delete_fb(chain->fbo_pass, chain->fbo); + if (chain->fbo) + gl2_delete_fb(chain->fbo_pass, chain->fbo); + if (chain->fbo_texture) + glDeleteTextures(chain->fbo_pass, chain->fbo_texture); memset(chain->fbo_texture, 0, sizeof(chain->fbo_texture)); memset(chain->fbo, 0, sizeof(chain->fbo)); From 30c824c30a913140bd3f7c56d0193246af127b4f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 08:54:57 +0200 Subject: [PATCH 350/517] Try to fix MSVC warning --- gfx/drivers/d3d10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index eb9ecd3026..2795a76e26 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -100,7 +100,7 @@ static void d3d10_overlay_set_alpha(void* data, unsigned index, float mod) static bool d3d10_overlay_load(void* data, const void* image_data, unsigned num_images) { D3D10_BUFFER_DESC desc; - int i; + unsigned i; d3d10_sprite_t* sprites; d3d10_video_t* d3d10 = (d3d10_video_t*)data; const struct texture_image* images = (const struct texture_image*)image_data; From 710570a408eb1ef367fcf9b3c26300d842651d25 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 17 Apr 2018 11:36:25 +0200 Subject: [PATCH 351/517] (MSVC 2013) Buildfix --- libretro-common/compat/compat_snprintf.c | 2 +- pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libretro-common/compat/compat_snprintf.c b/libretro-common/compat/compat_snprintf.c index 074cf8e1bf..7cfd2d7682 100644 --- a/libretro-common/compat/compat_snprintf.c +++ b/libretro-common/compat/compat_snprintf.c @@ -24,7 +24,7 @@ #ifdef _MSC_VER #include -#if _MSC_VER >= 1900 +#if _MSC_VER >= 1800 #include /* added for _vsnprintf_s and _vscprintf on VS2015 and VS2017 */ #endif #include diff --git a/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj b/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj index af289217aa..f5d5f6a6f6 100644 --- a/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj +++ b/pkg/msvc/msvc-2010/RetroArch-msvc2010.vcxproj @@ -113,7 +113,7 @@ - true + false $(DXSDK_DIR)Include;$(IncludePath) $(DXSDK_DIR)Lib\x86;$(LibraryPath) AllRules.ruleset @@ -121,7 +121,7 @@ - true + false $(DXSDK_DIR)Include;$(CG_INC_PATH);$(IncludePath) $(DXSDK_DIR)Lib\x86;$(CG_LIB_PATH);$(LibraryPath) AllRules.ruleset @@ -129,7 +129,7 @@ - true + false $(DXSDK_DIR)Include;$(IncludePath) $(DXSDK_DIR)Lib\x64;$(LibraryPath) AllRules.ruleset @@ -137,7 +137,7 @@ - true + false $(DXSDK_DIR)Include;$(CG_INC_PATH);$(IncludePath) $(DXSDK_DIR)Lib\x64;$(CG_LIB64_PATH);$(LibraryPath) AllRules.ruleset @@ -383,4 +383,4 @@ - + \ No newline at end of file From 712a2fcc1da9a90e174e24ff76b8f8308ef84cc6 Mon Sep 17 00:00:00 2001 From: Ryunam Date: Tue, 17 Apr 2018 17:53:24 +0200 Subject: [PATCH 352/517] Add toggle to show/hide Playlist tabs --- config.def.h | 1 + configuration.c | 1 + configuration.h | 1 + intl/msg_hash_ar.h | 4 ++++ intl/msg_hash_chs.h | 2 ++ intl/msg_hash_cht.h | 2 ++ intl/msg_hash_eo.h | 2 ++ intl/msg_hash_fr.h | 2 ++ intl/msg_hash_it.h | 2 ++ intl/msg_hash_lbl.h | 2 ++ intl/msg_hash_nl.h | 2 ++ intl/msg_hash_us.h | 4 ++++ intl/msg_hash_vn.h | 4 ++++ menu/cbs/menu_cbs_sublabel.c | 4 ++++ menu/drivers/xmb.c | 2 +- menu/menu_displaylist.c | 3 +++ menu/menu_setting.c | 15 +++++++++++++++ msg_hash.h | 1 + 18 files changed, 53 insertions(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 26c8425972..3d4a6ab152 100644 --- a/config.def.h +++ b/config.def.h @@ -297,6 +297,7 @@ static bool content_show_history = true; #ifdef HAVE_LIBRETRODB static bool content_show_add = true; #endif +static bool content_show_playlists = true; #ifdef HAVE_XMB static unsigned xmb_scale_factor = 100; diff --git a/configuration.c b/configuration.c index 5d72273a96..b98fd08d47 100644 --- a/configuration.c +++ b/configuration.c @@ -1312,6 +1312,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, #ifdef HAVE_LIBRETRODB SETTING_BOOL("content_show_add", &settings->bools.menu_content_show_add, true, content_show_add, false); #endif + SETTING_BOOL("content_show_playlists", &settings->bools.menu_content_show_playlists, true, content_show_playlists, false); SETTING_BOOL("menu_show_load_core", &settings->bools.menu_show_load_core, true, menu_show_load_core, false); SETTING_BOOL("menu_show_load_content", &settings->bools.menu_show_load_content, true, menu_show_load_content, false); SETTING_BOOL("menu_show_information", &settings->bools.menu_show_information, true, menu_show_information, false); diff --git a/configuration.h b/configuration.h index 22afea73b4..d4aeaa9f6d 100644 --- a/configuration.h +++ b/configuration.h @@ -158,6 +158,7 @@ typedef struct settings bool menu_content_show_netplay; bool menu_content_show_history; bool menu_content_show_add; + bool menu_content_show_playlists; bool menu_unified_controls; bool quick_menu_show_take_screenshot; bool quick_menu_show_save_load_state; diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index 38ad7fe49d..ee7de26592 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -1879,6 +1879,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_HISTORY, "Show History Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_ADD, "Show Import content Tab") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_PLAYLISTS, + "Show Playlist Tabs") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_FAVORITES, "Show Favorites Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_IMAGES, @@ -3081,6 +3083,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, "Show the recent history tab inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, "Show the import content tab inside the main menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS, + "Show playlist tabs inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, "Show startup screen in menu. This is automatically set to false after the program starts for the first time.") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index 2359b5ffd5..ef35928e1c 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -2909,6 +2909,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, "Show the recent history tab inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, "Show the import content tab inside the main menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS, + "Show playlist tabs inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, "Show startup screen in menu. This is automatically set to false after the program starts for the first time.") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index ee0585c832..7f9945dabd 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -2901,6 +2901,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, "Show the recent history tab inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, "Show the import content tab inside the main menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS, + "Show playlist tabs inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, "Show startup screen in menu. This is automatically set to false after the program starts for the first time.") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 2a3d41b3d4..ed258bbce0 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -2774,6 +2774,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, "Show the recent history tab inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, "Show the import content tab inside the main menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS, + "Show playlist tabs inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, "Show startup screen in menu. This is automatically set to false after the program starts for the first time.") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index 0b24993d02..0133bdec20 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -2939,6 +2939,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, "Affiche l'onglet de l'historique dans le menu principal.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, "Show the import content tab inside the main menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS, + "Show playlist tabs inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, "Show startup screen in menu. This is automatically set to false after the program starts for the first time.") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index 99f7321f26..d441d6b7e3 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -2991,6 +2991,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, "Mostra la colonna cronologia all'interno del menu principale.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, "Mostra la colonna importa contenuto all'interno del menu principale.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS, + "Mostra le colonne delle playlist all'interno del menu principale") MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, "Mostra la schermata di avvio nel menu. Questo viene automaticamente impostato su off dopo l'avvio del programma per la prima volta.") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 8f138c19d2..7942fd7b3e 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1253,6 +1253,8 @@ MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_HISTORY, "content_show_history") MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_ADD, "content_show_add") +MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_PLAYLISTS, + "content_show_playlists") MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_FAVORITES, "content_show_favorites") MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_IMAGES, diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index 6f7ec9d529..c6e4047d9c 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -2776,6 +2776,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, "Show the recent history tab inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, "Show the import content tab inside the main menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS, + "Show playlist tabs inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, "Show startup screen in menu. This is automatically set to false after the program starts for the first time.") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 90d8a2c09b..d0752f8240 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1897,6 +1897,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_HISTORY, "Show History Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_ADD, "Show Import content Tab") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_PLAYLISTS, + "Show Playlist Tabs") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_FAVORITES, "Show Favorites Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_IMAGES, @@ -3126,6 +3128,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, "Show the recent history tab inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, "Show the import content tab inside the main menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS, + "Show playlist tabs inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, "Show startup screen in menu. This is automatically set to false after the program starts for the first time.") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index efc435f26a..285acf1900 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -1761,6 +1761,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_HISTORY, "Display History Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_ADD, "Display Import content Tab") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_PLAYLISTS, + "Display Playlist Tabs") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_IMAGES, "Display Image Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_MUSIC, @@ -2935,6 +2937,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY, "Show the recent history tab inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD, "Show the import content tab inside the main menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS, + "Show playlist tabs inside the main menu.") MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN, "Show startup screen in menu. This is automatically set to false after the program starts for the first time.") MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY, diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index d9841a6294..7be57bf835 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -338,6 +338,7 @@ default_sublabel_macro(action_bind_sublabel_menu_settings_tab, default_sublabel_macro(action_bind_sublabel_menu_settings_tab_enable_password, MENU_ENUM_SUBLABEL_CONTENT_SHOW_SETTINGS_PASSWORD) default_sublabel_macro(action_bind_sublabel_menu_history_tab, MENU_ENUM_SUBLABEL_CONTENT_SHOW_HISTORY) default_sublabel_macro(action_bind_sublabel_menu_import_content_tab, MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD) +default_sublabel_macro(action_bind_sublabel_menu_playlist_tabs, MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS) default_sublabel_macro(action_bind_sublabel_main_menu_enable_settings, MENU_ENUM_SUBLABEL_XMB_MAIN_MENU_ENABLE_SETTINGS) default_sublabel_macro(action_bind_sublabel_rgui_show_start_screen, MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN) default_sublabel_macro(action_bind_sublabel_menu_header_opacity, MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY) @@ -699,6 +700,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_CONTENT_SHOW_ADD: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_import_content_tab); break; + case MENU_ENUM_LABEL_CONTENT_SHOW_PLAYLISTS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_playlist_tabs); + break; case MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_main_menu_enable_settings); break; diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 53ca07b3d7..cf61dfa30e 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -1990,7 +1990,7 @@ static void xmb_init_horizontal_list(xmb_handle_t *xmb) info.type_default = FILE_TYPE_PLAIN; info.enum_idx = MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST; - if (!string_is_empty(info.path)) + if (settings->bools.menu_content_show_playlists && !string_is_empty(info.path)) { if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL, &info)) { diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 16da491e9c..f0d7940b66 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5216,6 +5216,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_CONTENT_SHOW_ADD, PARSE_ONLY_BOOL, false); #endif + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CONTENT_SHOW_PLAYLISTS, + PARSE_ONLY_BOOL, false); menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_TIMEDATE_ENABLE, PARSE_ONLY_BOOL, false); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 4f8b30e6ad..219bed435d 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5724,6 +5724,21 @@ static bool setting_append_list( settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); #endif + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_content_show_playlists, + MENU_ENUM_LABEL_CONTENT_SHOW_PLAYLISTS, + MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_PLAYLISTS, + content_show_playlists, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); + #ifdef HAVE_MATERIALUI if (string_is_equal(settings->arrays.menu_driver, "glui")) { diff --git a/msg_hash.h b/msg_hash.h index 1ebe02b5a7..f55e29f338 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -774,6 +774,7 @@ enum msg_hash_enums MENU_LABEL(CONTENT_SHOW_NETPLAY), MENU_LABEL(CONTENT_SHOW_HISTORY), MENU_LABEL(CONTENT_SHOW_ADD), + MENU_LABEL(CONTENT_SHOW_PLAYLISTS), MENU_LABEL(XMB_RIBBON_ENABLE), MENU_LABEL(THUMBNAILS), MENU_LABEL(LEFT_THUMBNAILS), From b0e62f3ec6cc45fea18e7b7edc896da60dce7c6f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 17 Apr 2018 21:29:02 +0200 Subject: [PATCH 353/517] (D3D11) Add more feature levels (D3D11) If CreateDeviceAndSwapchain fails, error out gracefully --- gfx/drivers/d3d11.c | 49 ++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 207b289796..fe29c3406b 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -19,6 +19,7 @@ #include #include +#include #include #include "../../driver.h" @@ -609,9 +610,16 @@ d3d11_gfx_init(const video_info_t* video, const input_driver_t** input, void** i dxgi_input_driver(settings->arrays.input_joypad_driver, input, input_data); { - UINT flags = 0; - D3D_FEATURE_LEVEL requested_feature_level = D3D_FEATURE_LEVEL_11_0; - DXGI_SWAP_CHAIN_DESC desc = { 0 }; + UINT flags = 0; + D3D_FEATURE_LEVEL + requested_feature_levels[] = + { + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0 + }; + DXGI_SWAP_CHAIN_DESC desc = { 0 }; + UINT number_feature_levels = ARRAY_SIZE(requested_feature_levels); desc.BufferCount = 1; desc.BufferDesc.Width = d3d11->vp.full_width; @@ -636,18 +644,22 @@ d3d11_gfx_init(const video_info_t* video, const input_driver_t** input, void** i #endif if(cached_device_d3d11 && cached_context) { - IDXGIFactory* dxgiFactory = NULL; - IDXGIDevice* dxgiDevice = NULL; - IDXGIAdapter* adapter = NULL; + IDXGIFactory* dxgiFactory = NULL; + IDXGIDevice* dxgiDevice = NULL; + IDXGIAdapter* adapter = NULL; - d3d11->device = cached_device_d3d11; - d3d11->context = cached_context; + d3d11->device = cached_device_d3d11; + d3d11->context = cached_context; d3d11->supportedFeatureLevel = cached_supportedFeatureLevel; - d3d11->device->lpVtbl->QueryInterface(d3d11->device, uuidof(IDXGIDevice), (void**)&dxgiDevice); + d3d11->device->lpVtbl->QueryInterface( + d3d11->device, uuidof(IDXGIDevice), (void**)&dxgiDevice); dxgiDevice->lpVtbl->GetAdapter(dxgiDevice, &adapter); - adapter->lpVtbl->GetParent(adapter, uuidof(IDXGIFactory1), (void**)&dxgiFactory); - dxgiFactory->lpVtbl->CreateSwapChain(dxgiFactory, (IUnknown*)d3d11->device, &desc, (IDXGISwapChain**)&d3d11->swapChain); + adapter->lpVtbl->GetParent( + adapter, uuidof(IDXGIFactory1), (void**)&dxgiFactory); + dxgiFactory->lpVtbl->CreateSwapChain( + dxgiFactory, (IUnknown*)d3d11->device, + &desc, (IDXGISwapChain**)&d3d11->swapChain); dxgiFactory->lpVtbl->Release(dxgiFactory); adapter->lpVtbl->Release(adapter); @@ -655,10 +667,13 @@ d3d11_gfx_init(const video_info_t* video, const input_driver_t** input, void** i } else { - D3D11CreateDeviceAndSwapChain( - NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, &requested_feature_level, 1, - D3D11_SDK_VERSION, &desc, (IDXGISwapChain**)&d3d11->swapChain, &d3d11->device, - &d3d11->supportedFeatureLevel, &d3d11->context); + if (FAILED(D3D11CreateDeviceAndSwapChain( + NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, + requested_feature_levels, number_feature_levels, + D3D11_SDK_VERSION, &desc, + (IDXGISwapChain**)&d3d11->swapChain, &d3d11->device, + &d3d11->supportedFeatureLevel, &d3d11->context))) + goto error; } } @@ -677,12 +692,14 @@ d3d11_gfx_init(const video_info_t* video, const input_driver_t** input, void** i d3d11->viewport.Height = d3d11->vp.full_height; d3d11->resize_viewport = true; d3d11->vsync = video->vsync; - d3d11->format = video->rgb32 ? DXGI_FORMAT_B8G8R8X8_UNORM : DXGI_FORMAT_B5G6R5_UNORM; + d3d11->format = video->rgb32 ? + DXGI_FORMAT_B8G8R8X8_UNORM : DXGI_FORMAT_B5G6R5_UNORM; d3d11->frame.texture[0].desc.Format = d3d11->format; d3d11->frame.texture[0].desc.Usage = D3D11_USAGE_DEFAULT; d3d11->frame.texture[0].desc.Width = 4; d3d11->frame.texture[0].desc.Height = 4; + d3d11_init_texture(d3d11->device, &d3d11->frame.texture[0]); d3d11->menu.texture.desc.Usage = D3D11_USAGE_DEFAULT; From af87a19675e374587a844cd4951d7d4119e74f26 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Tue, 17 Apr 2018 23:25:26 -0500 Subject: [PATCH 354/517] Fix a silly bug in the code that tries copying the dll to 30 random files (should not return true at the end) --- runahead/secondary_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runahead/secondary_core.c b/runahead/secondary_core.c index b90c220649..016f53ca2e 100644 --- a/runahead/secondary_core.c +++ b/runahead/secondary_core.c @@ -139,6 +139,7 @@ failed: bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, ssize_t dataSize) { + bool okay = false; unsigned i; char number_buf[32]; const char *prefix = "tmp"; @@ -170,11 +171,14 @@ bool write_file_with_random_name(char **tempDllPath, strcat_alloc(tempDllPath, number_buf); strcat_alloc(tempDllPath, ext); if (filestream_write_file(*tempDllPath, data, dataSize)) + { + okay = true; break; + } } FREE(ext); - return true; + return okay; } bool secondary_core_create(void) From e17c3d869d6b6b14328d573a4219ed77dc3153f5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 18 Apr 2018 06:43:21 +0200 Subject: [PATCH 355/517] Avoid warnings on gx2_common.h --- gfx/common/gx2_common.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/gfx/common/gx2_common.h b/gfx/common/gx2_common.h index 3ab9d9eefe..4bab051469 100644 --- a/gfx/common/gx2_common.h +++ b/gfx/common/gx2_common.h @@ -2,11 +2,13 @@ #include -#include "gfx/drivers/gx2_shaders/frame.h" -#include "gfx/drivers/gx2_shaders/tex.h" -#include "gfx/drivers/gx2_shaders/sprite.h" -#include "gfx/drivers/gx2_shaders/menu_shaders.h" -#include "gfx/video_shader_parse.h" +#Include "../video_defines.h" +#include "../video_shader_parse.h" + +#include "../drivers/gx2_shaders/frame.h" +#include "../drivers/gx2_shaders/tex.h" +#include "../drivers/gx2_shaders/sprite.h" +#include "../drivers/gx2_shaders/menu_shaders.h" #undef _X #undef _B @@ -23,7 +25,6 @@ #define _1 0x05 #define GX2_COMP_SEL(c0, c1, c2, c3) (((c0) << 24) | ((c1) << 16) | ((c2) << 8) | (c3)) -#define COLOR_ABGR(r, g, b, a) (((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(g) << 8) | ((u32)(r) << 0)) #define COLOR_ARGB(r, g, b, a) (((u32)(a) << 24) | ((u32)(r) << 16) | ((u32)(g) << 8) | ((u32)(b) << 0)) #define COLOR_RGBA(r, g, b, a) (((u32)(r) << 24) | ((u32)(g) << 16) | ((u32)(b) << 8) | ((u32)(a) << 0)) From 9532a77b4f2701982e79c8f022c1c265e8c89a25 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 18 Apr 2018 07:53:10 +0200 Subject: [PATCH 356/517] Buildfix --- gfx/common/gx2_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/common/gx2_common.h b/gfx/common/gx2_common.h index 4bab051469..3b424f4b44 100644 --- a/gfx/common/gx2_common.h +++ b/gfx/common/gx2_common.h @@ -2,7 +2,7 @@ #include -#Include "../video_defines.h" +#include "../video_defines.h" #include "../video_shader_parse.h" #include "../drivers/gx2_shaders/frame.h" From 070ee2fb4048c688b93feff8637b96be39fc0d4b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 18 Apr 2018 08:03:34 +0200 Subject: [PATCH 357/517] (dispserv_win32.c) Simplify code --- gfx/display_servers/dispserv_win32.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/gfx/display_servers/dispserv_win32.c b/gfx/display_servers/dispserv_win32.c index 6bbc067dcd..d4514a9f90 100644 --- a/gfx/display_servers/dispserv_win32.c +++ b/gfx/display_servers/dispserv_win32.c @@ -143,9 +143,9 @@ static bool win32_set_window_progress(void *data, int progress, bool finished) { HWND hwnd = win32_get_window(); dispserv_win32_t *serv = (dispserv_win32_t*)data; - bool ret = false; - serv->progress = progress; + if (serv) + serv->progress = progress; #ifdef HAS_TASKBAR_EXT if (!g_taskbarList || !win32_taskbar_is_created()) @@ -154,37 +154,28 @@ static bool win32_set_window_progress(void *data, int progress, bool finished) if (progress == -1) { if (ITaskbarList3_SetProgressState( - g_taskbarList, hwnd, TBPF_INDETERMINATE) == S_OK) - ret = true; - - if (!ret) + g_taskbarList, hwnd, TBPF_INDETERMINATE) != S_OK) return false; } else if (finished) { if (ITaskbarList3_SetProgressState( - g_taskbarList, hwnd, TBPF_NOPROGRESS) == S_OK) - ret = true; - - if (!ret) + g_taskbarList, hwnd, TBPF_NOPROGRESS) != S_OK) return false; } else if (progress >= 0) { if (ITaskbarList3_SetProgressState( - g_taskbarList, hwnd, TBPF_NORMAL) == S_OK) - ret = true; - - if (!ret) + g_taskbarList, hwnd, TBPF_NORMAL) != S_OK) return false; if (ITaskbarList3_SetProgressValue( - g_taskbarList, hwnd, progress, 100) == S_OK) - ret = true; + g_taskbarList, hwnd, progress, 100) != S_OK) + return false; } #endif - return ret; + return true; } static bool win32_set_window_decorations(void *data, bool on) @@ -217,7 +208,7 @@ static bool win32_display_server_set_resolution(void *data, return false; if (f_restore == 0) - freq = hz; + freq = hz; EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); From a7ad6e20243c5a38a7118914ada95b8be6bf5628 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 18 Apr 2018 08:22:01 +0200 Subject: [PATCH 358/517] (XMB) Add icons for shader/shader presets --- menu/drivers/xmb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index cf61dfa30e..81d3432424 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -2355,6 +2355,9 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, break; } return xmb->textures.list[XMB_TEXTURE_FILE]; + case FILE_TYPE_SHADER: + case FILE_TYPE_SHADER_PRESET: + return xmb->textures.list[XMB_TEXTURE_SHADER_OPTIONS]; case FILE_TYPE_CARCHIVE: return xmb->textures.list[XMB_TEXTURE_ZIP]; case FILE_TYPE_MUSIC: @@ -2567,6 +2570,8 @@ static int xmb_draw_item( { if ( string_is_equal(entry->value, "...") || + string_is_equal(entry->value, "(PRESET)") || + string_is_equal(entry->value, "(SHADER)") || string_is_equal(entry->value, "(COMP)") || string_is_equal(entry->value, "(CORE)") || string_is_equal(entry->value, "(MOVIE)") || From 4914c164fa78e075cb32b115c4fe674ea16134d2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 19 Apr 2018 07:56:57 +0200 Subject: [PATCH 359/517] Update file_stream_transforms --- .../include/streams/file_stream_transforms.h | 15 ++++++++------- libretro-common/streams/file_stream_transforms.c | 10 +++++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/libretro-common/include/streams/file_stream_transforms.h b/libretro-common/include/streams/file_stream_transforms.h index 7919f6fbbd..77f52eb683 100644 --- a/libretro-common/include/streams/file_stream_transforms.h +++ b/libretro-common/include/streams/file_stream_transforms.h @@ -23,9 +23,10 @@ #ifndef __LIBRETRO_SDK_FILE_STREAM_TRANSFORMS_H #define __LIBRETRO_SDK_FILE_STREAM_TRANSFORMS_H +#include +#include #include #include -#include RETRO_BEGIN_DECLS @@ -61,19 +62,19 @@ RFILE* rfopen(const char *path, const char *mode); int rfclose(RFILE* stream); -long rftell(RFILE* stream); +int64_t rftell(RFILE* stream); -int rfseek(RFILE* stream, long offset, int origin); +int64_t rfseek(RFILE* stream, int64_t offset, int origin); -size_t rfread(void* buffer, - size_t elementSize, size_t elementCount, RFILE* stream); +int64_t rfread(void* buffer, + size_t elem_size, size_t elem_count, RFILE* stream); char *rfgets(char *buffer, int maxCount, RFILE* stream); int rfgetc(RFILE* stream); -size_t rfwrite(void const* buffer, - size_t elementSize, size_t elementCount, RFILE* stream); +int64_t rfwrite(void const* buffer, + size_t elem_size, size_t elem_count, RFILE* stream) int rfputc(int character, RFILE * stream); diff --git a/libretro-common/streams/file_stream_transforms.c b/libretro-common/streams/file_stream_transforms.c index c35a337388..efeb7edd9c 100644 --- a/libretro-common/streams/file_stream_transforms.c +++ b/libretro-common/streams/file_stream_transforms.c @@ -72,12 +72,12 @@ int rfclose(RFILE* stream) return filestream_close(stream); } -long rftell(RFILE* stream) +int64_t rftell(RFILE* stream) { return filestream_tell(stream); } -int rfseek(RFILE* stream, long offset, int origin) +int64_t rfseek(RFILE* stream, int64_t offset, int origin) { int seek_position = -1; switch (origin) @@ -93,10 +93,10 @@ int rfseek(RFILE* stream, long offset, int origin) break; } - return (int)filestream_seek(stream, (ssize_t)offset, seek_position); + return filestream_seek(stream, offset, seek_position); } -size_t rfread(void* buffer, +int64_t rfread(void* buffer, size_t elem_size, size_t elem_count, RFILE* stream) { return filestream_read(stream, buffer, elem_size * elem_count); @@ -112,7 +112,7 @@ int rfgetc(RFILE* stream) return filestream_getc(stream); } -size_t rfwrite(void const* buffer, +int64_t rfwrite(void const* buffer, size_t elem_size, size_t elem_count, RFILE* stream) { return filestream_write(stream, buffer, elem_size * elem_count); From 1c66905f0d8ab42d856d5277129ad5885b871c9e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 19 Apr 2018 08:01:07 +0200 Subject: [PATCH 360/517] Updates --- libretro-common/include/streams/file_stream_transforms.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretro-common/include/streams/file_stream_transforms.h b/libretro-common/include/streams/file_stream_transforms.h index 77f52eb683..c1690fa6d6 100644 --- a/libretro-common/include/streams/file_stream_transforms.h +++ b/libretro-common/include/streams/file_stream_transforms.h @@ -74,7 +74,7 @@ char *rfgets(char *buffer, int maxCount, RFILE* stream); int rfgetc(RFILE* stream); int64_t rfwrite(void const* buffer, - size_t elem_size, size_t elem_count, RFILE* stream) + size_t elem_size, size_t elem_count, RFILE* stream); int rfputc(int character, RFILE * stream); From 74d4bc80d97b690306a79d69c200e7f0c45edb21 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 19 Apr 2018 09:43:21 +0200 Subject: [PATCH 361/517] (libretro-common) Updates --- libretro-common/audio/dsp_filters/reverb.c | 82 +++++++++++----------- libretro-common/include/file/file_path.h | 4 +- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/libretro-common/audio/dsp_filters/reverb.c b/libretro-common/audio/dsp_filters/reverb.c index e81d85980d..e9bc8170e1 100644 --- a/libretro-common/audio/dsp_filters/reverb.c +++ b/libretro-common/audio/dsp_filters/reverb.c @@ -91,37 +91,13 @@ static const float initialwidth = 1; static const float initialmode = 0; static const float freezemode = 0.5f; -#define combtuningL1 1116 -#define combtuningL2 1188 -#define combtuningL3 1277 -#define combtuningL4 1356 -#define combtuningL5 1422 -#define combtuningL6 1491 -#define combtuningL7 1557 -#define combtuningL8 1617 -#define allpasstuningL1 556 -#define allpasstuningL2 441 -#define allpasstuningL3 341 -#define allpasstuningL4 225 - struct revmodel { struct comb combL[numcombs]; struct allpass allpassL[numallpasses]; - float bufcombL1[combtuningL1]; - float bufcombL2[combtuningL2]; - float bufcombL3[combtuningL3]; - float bufcombL4[combtuningL4]; - float bufcombL5[combtuningL5]; - float bufcombL6[combtuningL6]; - float bufcombL7[combtuningL7]; - float bufcombL8[combtuningL8]; - - float bufallpassL1[allpasstuningL1]; - float bufallpassL2[allpasstuningL2]; - float bufallpassL3[allpasstuningL3]; - float bufallpassL4[allpasstuningL4]; + float **bufcomb; + float **bufallpass; float gain; float roomsize, roomsize1; @@ -210,21 +186,29 @@ static void revmodel_setmode(struct revmodel *rev, float value) revmodel_update(rev); } -static void revmodel_init(struct revmodel *rev) +static void revmodel_init(struct revmodel *rev,int srate) { - rev->combL[0].buffer = rev->bufcombL1; rev->combL[0].bufsize = combtuningL1; - rev->combL[1].buffer = rev->bufcombL2; rev->combL[1].bufsize = combtuningL2; - rev->combL[2].buffer = rev->bufcombL3; rev->combL[2].bufsize = combtuningL3; - rev->combL[3].buffer = rev->bufcombL4; rev->combL[3].bufsize = combtuningL4; - rev->combL[4].buffer = rev->bufcombL5; rev->combL[4].bufsize = combtuningL5; - rev->combL[5].buffer = rev->bufcombL6; rev->combL[5].bufsize = combtuningL6; - rev->combL[6].buffer = rev->bufcombL7; rev->combL[6].bufsize = combtuningL7; - rev->combL[7].buffer = rev->bufcombL8; rev->combL[7].bufsize = combtuningL8; - rev->allpassL[0].buffer = rev->bufallpassL1; rev->allpassL[0].bufsize = allpasstuningL1; - rev->allpassL[1].buffer = rev->bufallpassL2; rev->allpassL[1].bufsize = allpasstuningL2; - rev->allpassL[2].buffer = rev->bufallpassL3; rev->allpassL[2].bufsize = allpasstuningL3; - rev->allpassL[3].buffer = rev->bufallpassL4; rev->allpassL[3].bufsize = allpasstuningL4; + static const int comb_lengths[8] = { 1116,1188,1277,1356,1422,1491,1557,1617 }; + static const int allpass_lengths[4] = { 225,341,441,556 }; + double r = srate * (1 / 44100.0); + unsigned c; + + rev->bufcomb=malloc(numcombs*sizeof(float*)); + for (c = 0; c < numcombs; ++c) + { + rev->bufcomb[c] = malloc(r*comb_lengths[c]*sizeof(float)); + rev->combL[c].buffer = rev->bufcomb[c]; + rev->combL[c].bufsize=r*comb_lengths[c]; + } + + rev->bufallpass=malloc(numallpasses*sizeof(float*)); + for (c = 0; c < numallpasses; ++c) + { + rev->bufallpass[c] = malloc(r*allpass_lengths[c]*sizeof(float)); + rev->allpassL[c].buffer = rev->bufallpass[c]; + rev->allpassL[c].bufsize=r*allpass_lengths[c]; + } rev->allpassL[0].feedback = 0.5f; rev->allpassL[1].feedback = 0.5f; @@ -246,6 +230,22 @@ struct reverb_data static void reverb_free(void *data) { + struct reverb_data *rev = (struct reverb_data*)data; + unsigned i; + + for (i = 0; i < numcombs; i++) { + free(rev->left.bufcomb[i]); + free(rev->right.bufcomb[i]); + } + free(rev->left.bufcomb); + free(rev->right.bufcomb); + + for (i = 0; i < numallpasses; i++) { + free(rev->left.bufallpass[i]); + free(rev->right.bufallpass[i]); + } + free(rev->left.bufallpass); + free(rev->right.bufallpass); free(data); } @@ -284,8 +284,8 @@ static void *reverb_init(const struct dspfilter_info *info, config->get_float(userdata, "roomwidth", &roomwidth, 0.56f); config->get_float(userdata, "roomsize", &roomsize, 0.56f); - revmodel_init(&rev->left); - revmodel_init(&rev->right); + revmodel_init(&rev->left,info->input_rate); + revmodel_init(&rev->right,info->input_rate); revmodel_setdamp(&rev->left, damping); revmodel_setdry(&rev->left, drytime); diff --git a/libretro-common/include/file/file_path.h b/libretro-common/include/file/file_path.h index a7ee1e7c54..89321c8f80 100644 --- a/libretro-common/include/file/file_path.h +++ b/libretro-common/include/file/file_path.h @@ -432,7 +432,7 @@ void path_basedir_wrapper(char *path); #endif /** - * path_default_slash: + * path_default_slash and path_default_slash_c: * * Gets the default slash separator. * @@ -440,8 +440,10 @@ void path_basedir_wrapper(char *path); */ #ifdef _WIN32 #define path_default_slash() "\\" +#define path_default_slash_c() '\\' #else #define path_default_slash() "/" +#define path_default_slash_c() '/' #endif /** From 8198e5c0b2fe28f935bd13cb352b7707c244b85a Mon Sep 17 00:00:00 2001 From: radius Date: Thu, 19 Apr 2018 09:54:16 -0500 Subject: [PATCH 362/517] fix #6596 --- input/input_remapping.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/input/input_remapping.c b/input/input_remapping.c index 964d78d8ab..f92eb20d86 100644 --- a/input/input_remapping.c +++ b/input/input_remapping.c @@ -221,7 +221,6 @@ bool input_remapping_save_file(const char *path) if (settings->uints.input_keymapper_ids[i][j] != RETROK_UNKNOWN) config_set_int(conf, key_ident[j], settings->uints.input_keymapper_ids[i][j]); - } else { @@ -237,7 +236,7 @@ bool input_remapping_save_file(const char *path) config_set_int(conf, stk_ident[k], -1); else - config_unset(conf,btn_ident[j]); + config_unset(conf, stk_ident[k]); } } snprintf(s1, sizeof(s1), "input_libretro_device_p%u", i + 1); From d8b5fea374666d7a1fe899b69f07d325f63ac18b Mon Sep 17 00:00:00 2001 From: radius Date: Thu, 19 Apr 2018 10:07:59 -0500 Subject: [PATCH 363/517] fix coverity warnings --- menu/cbs/menu_cbs_left.c | 2 +- menu/cbs/menu_cbs_right.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 6e306b3b97..c39289f671 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -403,7 +403,7 @@ static int action_left_input_desc_kbd(unsigned type, const char *label, if (key_id > 0) key_id--; else - key_id = RARCH_MAX_KEYS + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; + key_id = (RARCH_MAX_KEYS - 1) + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; settings->uints.input_keymapper_ids[offset][id] = key_descriptors[key_id].key; diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index fd5c9ff8f1..82b287240e 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -126,9 +126,7 @@ int action_right_input_desc_kbd(unsigned type, const char *label, break; } - RARCH_LOG("o:%u t:%u i:%u r:%u\n", offset, type, id, remap_id); - - if (key_id < RARCH_MAX_KEYS + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN) + if (key_id < (RARCH_MAX_KEYS - 1) + MENU_SETTINGS_INPUT_DESC_KBD_BEGIN) key_id++; else key_id = 0; From 835e491c620109b8065657ca41f42b8ce729c4f6 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 19 Apr 2018 22:26:40 +0200 Subject: [PATCH 364/517] Use int64_t type for feeding to filestream_read --- libretro-common/formats/libchdr/chd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libretro-common/formats/libchdr/chd.c b/libretro-common/formats/libchdr/chd.c index be359615a1..32e9dd1045 100644 --- a/libretro-common/formats/libchdr/chd.c +++ b/libretro-common/formats/libchdr/chd.c @@ -38,6 +38,7 @@ ***************************************************************************/ #include +#include #include #include #include @@ -1876,7 +1877,7 @@ static UINT32 header_guess_unitbytes(chd_file *chd) static chd_error header_read(chd_file *chd, chd_header *header) { UINT8 rawheader[CHD_MAX_HEADER_SIZE]; - UINT32 count; + int64_t count; /* punt if NULL */ if (header == NULL) From 4d64a94cb21a9df820cebcc9a5cc5b227379751f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 19 Apr 2018 22:28:17 +0200 Subject: [PATCH 365/517] Updates --- libretro-common/include/libretro.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 804a6c26e6..654c799828 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -599,9 +599,12 @@ enum retro_mod * GET_VARIABLE. * This allows the frontend to present these variables to * a user dynamically. - * This should be called as early as possible (ideally in - * retro_set_environment). - * + * This should be called the first time as early as + * possible (ideally in retro_set_environment). + * Afterward it may be called again for the core to communicate + * updated options to the frontend, but the number of core + * options must not change from the number in the initial call. + * * 'data' points to an array of retro_variable structs * terminated by a { NULL, NULL } element. * retro_variable::key should be namespaced to not collide From d8fd499f3d52054c778da5e5e340f7ad16930520 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 19 Apr 2018 23:41:15 +0200 Subject: [PATCH 366/517] Update count --- libretro-common/formats/libchdr/chd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libretro-common/formats/libchdr/chd.c b/libretro-common/formats/libchdr/chd.c index 32e9dd1045..bb8e91de11 100644 --- a/libretro-common/formats/libchdr/chd.c +++ b/libretro-common/formats/libchdr/chd.c @@ -1706,7 +1706,7 @@ chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, { metadata_entry metaentry; chd_error err; - UINT32 count; + int64_t count; /* if we didn't find it, just return */ err = metadata_find_entry(chd, searchtag, searchindex, &metaentry); @@ -2240,7 +2240,7 @@ static chd_error map_read(chd_file *chd) UINT8 raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; UINT64 fileoffset, maxoffset = 0; UINT8 cookie[MAP_ENTRY_SIZE]; - UINT32 count; + int64_t count; chd_error err; int i; From 16df2ceb4eff8e70a00a7ca61d36eb20b19f18fc Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 17 Apr 2018 11:39:59 +0200 Subject: [PATCH 367/517] Fix Xbox OG --- gfx/drivers/d3d8.c | 6 ++---- gfx/drivers_font/xdk1_xfonts.c | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index 60e87e3a3d..67e7aca4df 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -1005,7 +1005,7 @@ static void d3d8_set_nonblock_state(void *data, bool state) #ifdef _XBOX d3d8_set_render_state(d3d->dev, D3D8_PRESENTATIONINTERVAL, interval ? - D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; + D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE ); #else d3d->needs_restore = true; @@ -1511,9 +1511,7 @@ static void d3d8_get_overlay_interface(void *data, static void d3d8_update_title(video_frame_info_t *video_info) { -#ifdef _XBOX - const ui_window_t *window = NULL; -#else +#ifndef _XBOX const ui_window_t *window = ui_companion_driver_get_window_ptr(); #endif diff --git a/gfx/drivers_font/xdk1_xfonts.c b/gfx/drivers_font/xdk1_xfonts.c index ccfc7b8542..3e7e425e81 100644 --- a/gfx/drivers_font/xdk1_xfonts.c +++ b/gfx/drivers_font/xdk1_xfonts.c @@ -22,8 +22,8 @@ #endif #include "../drivers/d3d.h" -#include "../drivers/d3d_common.h" -#include "../drivers/d3d8_common.h" +#include "../common/d3d_common.h" +#include "../common/d3d8_common.h" #include "../font_driver.h" From 53738e4a0d5c7d0a74a4d330596c651790134c57 Mon Sep 17 00:00:00 2001 From: gblues Date: Wed, 18 Apr 2018 23:12:45 -0700 Subject: [PATCH 368/517] Allow Wii U GCA to work without 2nd cable attached === DETAILS So, the GCA has 2 USB connections; one is the data connection, and the second is used to drive rumble. Due to a driver bug, if the second cable wasn't attached, the pads wouldn't get detected. I fixed that bug. --- input/common/hid/device_wiiu_gca.c | 45 ++++++++++++++++-------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 9e09598022..fe27e764f5 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -23,8 +23,9 @@ static uint8_t activation_packet[] = { 0x13 }; #endif #define GCA_PORT_INITIALIZING 0x00 -#define GCA_PORT_EMPTY 0x04 -#define GCA_PORT_CONNECTED 0x14 +#define GCA_PORT_POWERED 0x04 +#define GCA_PORT_CONNECTED 0x10 + typedef struct wiiu_gca_instance { void *handle; @@ -107,40 +108,41 @@ static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size) static void update_pad_state(wiiu_gca_instance_t *instance) { int i, port; + unsigned char port_connected; + if(!instance || !instance->online) return; joypad_connection_t *pad; /* process each pad */ + //RARCH_LOG_BUFFER(instance->device_state, 37); for(i = 1; i < 37; i += 9) { port = i / 9; pad = instance->pads[port]; - switch(instance->device_state[i]) + port_connected = instance->device_state[i] & GCA_PORT_CONNECTED; + + if(port_connected) { - case GCA_PORT_INITIALIZING: - case GCA_PORT_EMPTY: - if(pad != NULL) { - RARCH_LOG("[gca]: Gamepad at port %d disconnected.\n", port+1); - unregister_pad(instance, port); - } - break; - case GCA_PORT_CONNECTED: + if(pad == NULL) + { + RARCH_LOG("[gca]: Gamepad at port %d connected.\n", port+1); + instance->pads[port] = hid_pad_register(instance, &wiiu_gca_pad_connection); + pad = instance->pads[port]; if(pad == NULL) { - RARCH_LOG("[gca]: Gamepad at port %d connected.\n", port+1); - instance->pads[port] = hid_pad_register(instance, &wiiu_gca_pad_connection); - pad = instance->pads[port]; - if(pad == NULL) - { - RARCH_ERR("[gca]: Failed to register pad.\n"); - break; - } + RARCH_ERR("[gca]: Failed to register pad.\n"); + break; } + } - pad->iface->packet_handler(pad->data, &instance->device_state[i], 9); - break; + pad->iface->packet_handler(pad->data, &instance->device_state[i], 9); + } else { + if(pad != NULL) { + RARCH_LOG("[gca]: Gamepad at port %d disconnected.\n", port+1); + unregister_pad(instance, port); + } } } } @@ -263,6 +265,7 @@ static int16_t wiiu_gca_get_axis(void *data, unsigned axis) if(val > 0x1000 || val < -0x1000) return 0; +// RARCH_LOG("[gca]: reading axis %d: %04x\n", axis, val); return val; } From 0c92fab0b9c5abf4df09493a7506027ab304aa9f Mon Sep 17 00:00:00 2001 From: gblues Date: Fri, 20 Apr 2018 00:00:33 -0700 Subject: [PATCH 369/517] Fix GameCube button detection This should fix the issue where R/L buttons didn't register when doing input detection. This also brings the GC pad in line with the rest of the gamepads in input_autodetect_builtin.c. Also fixed a really stupid bug that was part of why analog inputs aren't being read. Analog still isn't working, mind, but it's a lot closer to working now that it's actually getting down into the pad driver level! --- input/common/hid/device_wiiu_gca.c | 53 +++++++++++++++++++++++++++--- input/input_autodetect_builtin.c | 24 +++++++------- wiiu/input/hidpad_driver.c | 4 +-- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index fe27e764f5..ab113494c4 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -212,6 +212,38 @@ static void wiiu_gca_get_buttons(void *data, input_bits_t *state) } } +static void update_buttons(gca_pad_t *pad) +{ + uint32_t i, pressed_keys; + + static const uint32_t button_mapping[12] = + { + RETRO_DEVICE_ID_JOYPAD_A, + RETRO_DEVICE_ID_JOYPAD_B, + RETRO_DEVICE_ID_JOYPAD_X, + RETRO_DEVICE_ID_JOYPAD_Y, + RETRO_DEVICE_ID_JOYPAD_LEFT, + RETRO_DEVICE_ID_JOYPAD_RIGHT, + RETRO_DEVICE_ID_JOYPAD_DOWN, + RETRO_DEVICE_ID_JOYPAD_UP, + RETRO_DEVICE_ID_JOYPAD_START, + RETRO_DEVICE_ID_JOYPAD_SELECT, + RETRO_DEVICE_ID_JOYPAD_R, + RETRO_DEVICE_ID_JOYPAD_L, + }; + + if(!pad) + return; + + pressed_keys = pad->data[1] | (pad->data[2] << 8); + pad->buttons = 0; + + for(i = 0; i < 12; i++) + pad->buttons |= (pressed_keys & (1 << i)) ? + (1 << button_mapping[i]) : 0; +} + + /** * The USB packet provides a 9-byte data packet for each pad. * @@ -230,9 +262,11 @@ static void wiiu_gca_packet_handler(void *data, uint8_t *packet, uint16_t size) return; memcpy(pad->data, packet, size); - pad->buttons = pad->data[1] | (pad->data[2] << 8); + update_buttons(pad); } + + static void wiiu_gca_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) { (void)data; @@ -240,8 +274,19 @@ static void wiiu_gca_set_rumble(void *data, enum retro_rumble_effect effect, uin (void)strength; } +static unsigned decode_axis(unsigned axis) +{ + unsigned positive = AXIS_POS_GET(axis); + unsigned negative = AXIS_NEG_GET(axis); + RARCH_LOG("[gca]: positive: %ud negative: %ud\n", positive, negative); + + return positive >= 4 ? negative : positive; +} + static int16_t wiiu_gca_get_axis(void *data, unsigned axis) { + axis = decode_axis(axis); + RARCH_LOG("[gca]: decoded axis: %d\n", axis); int16_t val; gca_pad_t *pad = (gca_pad_t *)data; @@ -265,7 +310,7 @@ static int16_t wiiu_gca_get_axis(void *data, unsigned axis) if(val > 0x1000 || val < -0x1000) return 0; -// RARCH_LOG("[gca]: reading axis %d: %04x\n", axis, val); + RARCH_LOG("[gca]: reading axis %d: %04x\n", axis, val); return val; } @@ -288,10 +333,10 @@ static bool wiiu_gca_button(void *data, uint16_t joykey) { gca_pad_t *pad = (gca_pad_t *)data; - if(!pad) + if(!pad || joykey > 31) return false; - return (pad->buttons & joykey); + return pad->buttons & (1 << joykey); } pad_connection_interface_t wiiu_gca_pad_connection = { diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index 8b37d0828d..72909ebb8e 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -229,18 +229,18 @@ DECL_AXIS(r_y_minus, +3) #ifdef WIIU #define WIIUINPUT_GAMECUBE_DEFAULT_BINDS \ -DECL_BTN_EX(a, 0x0001, "A") \ -DECL_BTN_EX(b, 0x0002, "B") \ -DECL_BTN_EX(x, 0x0004, "X") \ -DECL_BTN_EX(y, 0x0008, "Y") \ -DECL_BTN_EX(select, 0x0200, "Z") \ -DECL_BTN_EX(start, 0x0100, "Start/Pause") \ -DECL_BTN_EX(l, 0x0800, "L Trigger") \ -DECL_BTN_EX(r, 0x0400, "R Trigger") \ -DECL_BTN_EX(left, 0x0010, "D-Pad Left") \ -DECL_BTN_EX(right, 0x0020, "D-Pad Right") \ -DECL_BTN_EX(down, 0x0040, "D-Pad Down") \ -DECL_BTN_EX(up, 0x0080, "D-Pad Up") \ +DECL_BTN_EX(a, 8, "A") \ +DECL_BTN_EX(b, 0, "B") \ +DECL_BTN_EX(x, 9, "X") \ +DECL_BTN_EX(y, 1, "Y") \ +DECL_BTN_EX(left, 6, "D-Pad Left") \ +DECL_BTN_EX(right, 7, "D-Pad Right") \ +DECL_BTN_EX(down, 5, "D-Pad Down") \ +DECL_BTN_EX(up, 4, "D-Pad Up") \ +DECL_BTN_EX(start, 3, "Start/Pause") \ +DECL_BTN_EX(select, 2, "Z") \ +DECL_BTN_EX(r, 10, "R Trigger") \ +DECL_BTN_EX(l, 11, "L Trigger") \ DECL_AXIS_EX(l_x_plus, +1, "Analog right") \ DECL_AXIS_EX(l_x_minus, -1, "Analog left") \ DECL_AXIS_EX(l_y_plus, +0, "Analog up") \ diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c index 9c0889b177..03efe64d5c 100644 --- a/wiiu/input/hidpad_driver.c +++ b/wiiu/input/hidpad_driver.c @@ -80,8 +80,8 @@ static void hidpad_get_buttons(unsigned pad, input_bits_t *state) static int16_t hidpad_axis(unsigned pad, uint32_t axis) { - if (!hidpad_query_pad(pad)); - return 0; + if (!hidpad_query_pad(pad)) + return 0; return HID_AXIS(pad, axis); } From ef3674485978d3dda99a7c753c18a5d84adf4998 Mon Sep 17 00:00:00 2001 From: gblues Date: Fri, 20 Apr 2018 13:03:53 -0700 Subject: [PATCH 370/517] Start implementing fall-back to async read == DETAILS The Wii U GC adapter doesn't seem to like doing async reads if it is connected via a USB hub. It seems to be device-specific, though, because my DS3 works just fine through the same hub. I tried creating a fallback to synchronous reads, but it resulted in a hard lock of the system. So, for the time being, it's going to be a known limitation. Might be solved by using a powered USB hub. Learned that the cache alignment is 64, not 32, so the alignment math has been updated. Thanks, @aliaspider for that info. --- wiiu/input/wiiu_hid.c | 147 +++++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 65 deletions(-) diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index 8a0d2fe53a..962e9323d8 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -20,6 +20,8 @@ static wiiu_event_list events; static wiiu_adapter_list adapters; +static void report_hid_error(const char *msg, wiiu_adapter_t *adapter, int32_t error); + static bool wiiu_hid_joypad_query(void *data, unsigned slot) { wiiu_hid_t *hid = (wiiu_hid_t *)data; @@ -222,15 +224,20 @@ static int32_t wiiu_hid_set_protocol(void *data, uint8_t protocol) static int32_t wiiu_hid_read(void *data, void *buffer, size_t size) { - wiiu_adapter_t *adapter = (wiiu_adapter_t *)data; + wiiu_adapter_t *adapter = (wiiu_adapter_t *)data; + int32_t result; - if(!adapter) - return -1; + if(!adapter) + return -1; - if(size > adapter->rx_size) - return -1; + if(size > adapter->rx_size) + return -1; - return HIDRead(adapter->handle, buffer, size, NULL, NULL); + result = HIDRead(adapter->handle, buffer, size, NULL, NULL); + if(result < 0) + report_hid_error("read failed", adapter, result); + + return result; } @@ -488,50 +495,12 @@ static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, if(error < 0) { - int16_t hid_error_code = error & 0xffff; - switch(hid_error_code) - { - case -100: - RARCH_ERR("[hid]: Invalid RM command (%s)\n", adapter->driver->name); - break; - case -102: - RARCH_ERR("[hid]: Invalid IOCTL command (%s)\n", adapter->driver->name); - break; - case -103: - RARCH_ERR("[hid]: bad vector count (%s)\n", adapter->driver->name); - break; - case -104: - RARCH_ERR("[hid]: invalid memory bank (%s)\n", adapter->driver->name); - break; - case -105: - RARCH_ERR("[hid]: invalid memory alignment (%s)\n", adapter->driver->name); - break; - case -106: - RARCH_ERR("[hid]: invalid data size (%s)\n", adapter->driver->name); - break; - case -107: - RARCH_ERR("[hid]: request cancelled (%s)\n", adapter->driver->name); - break; - case -108: - RARCH_ERR("[hid]: request timed out (%s)\n", adapter->driver->name); - break; - case -109: - RARCH_ERR("[hid]: request aborted (%s)\n", adapter->driver->name); - break; - case -110: - RARCH_ERR("[hid]: client priority error (%s)\n", adapter->driver->name); - break; - case -111: - RARCH_ERR("[hid]: invalid device handle (%s)\n", adapter->driver->name); - break; - } + report_hid_error("async read failed", adapter, error); } if(adapter->state == ADAPTER_STATE_READING) { adapter->state = ADAPTER_STATE_READY; - /* "error" usually is something benign like "device not ready", at - * least from my own experiments. Just ignore the error and retry - * the read. */ + if(error == 0) { adapter->driver->handle_packet(adapter->driver_handle, buffer, buffer_size); @@ -539,6 +508,58 @@ static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, } } +static void report_hid_error(const char *msg, wiiu_adapter_t *adapter, int32_t error) +{ + if(error >= 0) + return; + + int16_t hid_error_code = error & 0xffff; + int16_t error_category = (error >> 16) & 0xffff; + const char *device = (adapter && adapter->driver) ? adapter->driver->name : "unknown"; + + switch(hid_error_code) + { + case -100: + RARCH_ERR("[hid]: Invalid RM command (%s)\n", device); + break; + case -102: + RARCH_ERR("[hid]: Invalid IOCTL command (%s)\n", device); + break; + case -103: + RARCH_ERR("[hid]: bad vector count (%s)\n", device); + break; + case -104: + RARCH_ERR("[hid]: invalid memory bank (%s)\n", device); + break; + case -105: + RARCH_ERR("[hid]: invalid memory alignment (%s)\n", device); + break; + case -106: + RARCH_ERR("[hid]: invalid data size (%s)\n", device); + break; + case -107: + RARCH_ERR("[hid]: request cancelled (%s)\n", device); + break; + case -108: + RARCH_ERR("[hid]: request timed out (%s)\n", device); + break; + case -109: + RARCH_ERR("[hid]: request aborted (%s)\n", device); + break; + case -110: + RARCH_ERR("[hid]: client priority error (%s)\n", device); + break; + case -111: + RARCH_ERR("[hid]: invalid device handle (%s)\n", device); + break; +#if 0 + default: + RARCH_ERR("[hid]: Unknown error (%d:%d: %s)\n", + error_category, hid_error_code, device); +#endif + } +} + /** * Block until all the HIDRead() calls have returned. */ @@ -603,6 +624,18 @@ static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list) } } +static void wiiu_poll_adapter(wiiu_adapter_t *adapter) +{ + if(!adapter->connected) { + adapter->state = ADAPTER_STATE_DONE; + return; + } + + adapter->state = ADAPTER_STATE_READING; + HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, + wiiu_hid_read_loop_callback, adapter); +} + static void wiiu_poll_adapters(wiiu_hid_t *hid) { wiiu_adapter_t *it; @@ -611,19 +644,7 @@ static void wiiu_poll_adapters(wiiu_hid_t *hid) for(it = adapters.list; it != NULL; it = it->next) { if(it->state == ADAPTER_STATE_READY) - { - if(it->connected) - { - it->state = ADAPTER_STATE_READING; - HIDRead(it->handle, - it->rx_buffer, - it->rx_size, - wiiu_hid_read_loop_callback, - it); - } else { - it->state = ADAPTER_STATE_DONE; - } - } + wiiu_poll_adapter(it); if(it->state == ADAPTER_STATE_DONE) it->state = ADAPTER_STATE_GC; @@ -698,13 +719,9 @@ static void delete_hidclient(HIDClient *client) static void init_cachealigned_buffer(int32_t min_size, uint8_t **out_buf_ptr, int32_t *actual_size) { - int cacheblocks = (min_size < 32) ? 1 : min_size / 32; - if(min_size > 32 && min_size % 32 != 0) - cacheblocks++; + *actual_size = (min_size + 0x3f) & ~0x3f; - *actual_size = 32 * cacheblocks; - - *out_buf_ptr = alloc_zeroed(32, *actual_size); + *out_buf_ptr = alloc_zeroed(64, *actual_size); } static wiiu_adapter_t *new_adapter(wiiu_attach_event *event) From dae0f36a20b7ca8b962af583e07dbdc1ce81d13c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 15:00:35 +0200 Subject: [PATCH 371/517] Some cleanups --- gfx/drivers/gl.c | 8 ++++---- gfx/video_driver.c | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index ef6be56ece..ffb6073600 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2588,10 +2588,10 @@ static void gl_set_coords(void *handle_data, void *shader_data, static float gl_get_refresh_rate(void *data) { - float refresh_rate; - if (video_context_driver_get_refresh_rate(&refresh_rate)) - return refresh_rate; - return 0.0f; + float refresh_rate = 0.0f; + if (video_context_driver_get_refresh_rate(&refresh_rate)) + return refresh_rate; + return 0.0f; } static void gl_set_mvp(void *data, void *shader_data, diff --git a/gfx/video_driver.c b/gfx/video_driver.c index f18fd34b71..7e2b388e61 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -3194,8 +3194,12 @@ bool video_context_driver_get_refresh_rate(float *refresh_rate) { if (!current_video_context.get_refresh_rate || !refresh_rate) return false; + if (!video_context_data) + return false; - *refresh_rate = current_video_context.get_refresh_rate(video_context_data); + if (refresh_rate) + *refresh_rate = + current_video_context.get_refresh_rate(video_context_data); return true; } From 413914a1cf7225a3d15f08c244eef1ccb78cfff2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 15:55:05 +0200 Subject: [PATCH 372/517] Add Rewind/Latency/Overlay Settings to Quick Menu --- menu/menu_displaylist.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index f0d7940b66..ad81b13855 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -2684,6 +2684,24 @@ static int menu_displaylist_parse_load_content_settings( MENU_SETTING_ACTION, 0, 0); } + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ONSCREEN_OVERLAY_SETTINGS), + msg_hash_to_str(MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS), + MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS, + MENU_SETTING_ACTION, 0, 0); + + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_REWIND_SETTINGS), + msg_hash_to_str(MENU_ENUM_LABEL_REWIND_SETTINGS), + MENU_ENUM_LABEL_REWIND_SETTINGS, + MENU_SETTING_ACTION, 0, 0); + + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_LATENCY_SETTINGS), + msg_hash_to_str(MENU_ENUM_LABEL_LATENCY_SETTINGS), + MENU_ENUM_LABEL_LATENCY_SETTINGS, + MENU_SETTING_ACTION, 0, 0); + #if 0 menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS), From f4c83157af7797c8e16abaf47b5a3fba3cdb7b55 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Sat, 21 Apr 2018 18:06:14 +0200 Subject: [PATCH 373/517] Fix CocoaGL context driver --- gfx/drivers_context/cocoa_gl_ctx.m | 1 + 1 file changed, 1 insertion(+) diff --git a/gfx/drivers_context/cocoa_gl_ctx.m b/gfx/drivers_context/cocoa_gl_ctx.m index 8c137375a0..a95478f59d 100644 --- a/gfx/drivers_context/cocoa_gl_ctx.m +++ b/gfx/drivers_context/cocoa_gl_ctx.m @@ -666,6 +666,7 @@ const gfx_ctx_driver_t gfx_ctx_cocoagl = { cocoagl_gfx_ctx_swap_interval, cocoagl_gfx_ctx_set_video_mode, cocoagl_gfx_ctx_get_video_size, + NULL, /* get_refresh_rate */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ From 1526c8a15dc01702c18700cc6736511893109c99 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 18:05:25 +0200 Subject: [PATCH 374/517] (PS3) Should fix PS3 build with context driver --- gfx/drivers_context/ps3_ctx.c | 2 +- gfx/drivers_context/x_ctx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gfx/drivers_context/ps3_ctx.c b/gfx/drivers_context/ps3_ctx.c index 1a5dd2c0ff..72d1587cb6 100644 --- a/gfx/drivers_context/ps3_ctx.c +++ b/gfx/drivers_context/ps3_ctx.c @@ -418,10 +418,10 @@ const gfx_ctx_driver_t gfx_ctx_ps3 = { gfx_ctx_ps3_set_swap_interval, gfx_ctx_ps3_set_video_mode, gfx_ctx_ps3_get_video_size, + NULL, /* get_refresh_rate */ gfx_ctx_ps3_get_video_output_size, gfx_ctx_ps3_get_video_output_prev, gfx_ctx_ps3_get_video_output_next, - NULL, /* get_refresh_rate */ NULL, /* get_metrics */ NULL, NULL, /* update_title */ diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index 62051b43d8..bc906021bb 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -1225,7 +1225,7 @@ const gfx_ctx_driver_t gfx_ctx_x = { gfx_ctx_x_swap_interval, gfx_ctx_x_set_video_mode, x11_get_video_size, - x11_get_refresh_rate, /* get_refresh_rate */ + x11_get_refresh_rate, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ From 248f1242dc7b39ddd7315af8861ab600ae55d64e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 21 Apr 2018 18:14:19 +0200 Subject: [PATCH 375/517] Some warning fixes --- gfx/common/d3d10_common.c | 3 ++- gfx/drivers/d3d10.c | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/gfx/common/d3d10_common.c b/gfx/common/d3d10_common.c index bca16016b3..1b145015f7 100644 --- a/gfx/common/d3d10_common.c +++ b/gfx/common/d3d10_common.c @@ -142,7 +142,8 @@ void d3d10_update_texture( d3d10_texture_t* texture) { D3D10_MAPPED_TEXTURE2D mapped_texture; - D3D10_BOX frame_box = { 0, 0, 0, width, height, 1 }; + D3D10_BOX frame_box = { 0, 0, 0, (UINT)width, + (UINT)height, 1 }; if (!texture || !texture->staging) return; diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 2795a76e26..6938b2151a 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -303,8 +303,9 @@ static bool d3d10_gfx_set_shader(void* data, { #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) unsigned i; - d3d10_texture_t* source; - d3d10_video_t* d3d10 = (d3d10_video_t*)data; + config_file_t* conf = NULL; + d3d10_texture_t* source = NULL; + d3d10_video_t* d3d10 = (d3d10_video_t*)data; if (!d3d10) return false; @@ -321,7 +322,7 @@ static bool d3d10_gfx_set_shader(void* data, return false; } - config_file_t* conf = config_file_new(path); + conf = config_file_new(path); if (!conf) return false; From e325c6874842131960048b145d59d2d45ecd869d Mon Sep 17 00:00:00 2001 From: meleu Date: Sun, 22 Apr 2018 00:52:09 -0300 Subject: [PATCH 376/517] fixing cheevos hardcore mode description Removing fast-forward as something that is disabled when in Hardcore mode is enabled. Fast-forwarding is allowed even in hardcore mode. --- intl/msg_hash_ar.h | 2 +- intl/msg_hash_eo.h | 2 +- intl/msg_hash_nl.h | 2 +- intl/msg_hash_pt_br.h | 2 +- intl/msg_hash_us.h | 2 +- intl/msg_hash_vn.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index ee7de26592..1561f02d06 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -1904,7 +1904,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, "Enable or disable unofficial achievements and/or beta features for testing purposes.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, - "Enable or disable savestates, cheats, rewind, fast-forward, pause, and slow-motion for all games.") + "Enable or disable savestates, cheats, rewind, pause, and slow-motion for all games.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_LEADERBOARDS_ENABLE, "Enable or disable in-game leaderboards. Has no effect if Hardcore Mode is disabled.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_BADGES_ENABLE, diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index ed258bbce0..227c42163f 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -1665,7 +1665,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, "Enable or disable unofficial achievements and/or beta features for testing purposes.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, - "Enable or disable savestates, cheats, rewind, fast-forward, pause, and slow-motion for all games.") + "Enable or disable savestates, cheats, rewind, pause, and slow-motion for all games.") MSG_HASH(MENU_ENUM_SUBLABEL_DRIVER_SETTINGS, "Change drivers for this system.") MSG_HASH(MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS, diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index c6e4047d9c..eeb0cb534d 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -1663,7 +1663,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, "Enable or disable unofficial achievements and/or beta features for testing purposes.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, - "Enable or disable savestates, cheats, rewind, fast-forward, pause, and slow-motion for all games.") + "Enable or disable savestates, cheats, rewind, pause, and slow-motion for all games.") MSG_HASH(MENU_ENUM_SUBLABEL_DRIVER_SETTINGS, "Change drivers for this system.") MSG_HASH(MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS, diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index 8a6b593ab7..a81b17c9bf 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -2411,7 +2411,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, "Habilitar ou desabilitar conquistas não oficiais e/ou recursos beta para fins de teste." ) MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, - "Habilitar ou desabilitar Estado de Jogo, Trapaças, Voltar Atrás, Avanço Rápido, Pausa e Câmera Lenta para todos os jogos.") + "Habilitar ou desabilitar Estado de Jogo, Trapaças, Voltar Atrás, Pausa e Câmera Lenta para todos os jogos.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_LEADERBOARDS_ENABLE, "Ativar ou desativar tabelas de classificação no jogo. Não tem efeito se o modo Hardcore estiver desativado.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_BADGES_ENABLE, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index d0752f8240..1c3ea5e0ee 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1922,7 +1922,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, "Enable or disable unofficial achievements and/or beta features for testing purposes.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, - "Enable or disable savestates, cheats, rewind, fast-forward, pause, and slow-motion for all games.") + "Enable or disable savestates, cheats, rewind, pause, and slow-motion for all games.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_LEADERBOARDS_ENABLE, "Enable or disable in-game leaderboards. Has no effect if Hardcore Mode is disabled.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_BADGES_ENABLE, diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index 285acf1900..d3cc3bee25 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -1784,7 +1784,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL, "Enable or disable unofficial achievements and/or beta features for testing purposes.") MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE, - "Enable or disable savestates, cheats, rewind, fast-forward, pause, and slow-motion for all games.") + "Enable or disable savestates, cheats, rewind, pause, and slow-motion for all games.") MSG_HASH(MENU_ENUM_SUBLABEL_DRIVER_SETTINGS, "Change drivers used by the system.") MSG_HASH(MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS, From d4c691527a844bcc15a166ce8077550f65448e4e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 11:57:49 +0200 Subject: [PATCH 377/517] (3DS/Emscripten/Vita/Wiiu) Add HAVE_CHD support --- Makefile.ctr | 2 ++ Makefile.emscripten | 1 + Makefile.vita | 2 ++ Makefile.wiiu | 2 ++ 4 files changed, 7 insertions(+) diff --git a/Makefile.ctr b/Makefile.ctr index d47cc66c25..40b3cf160b 100644 --- a/Makefile.ctr +++ b/Makefile.ctr @@ -47,9 +47,11 @@ ifeq ($(GRIFFIN_BUILD), 1) DEFINES += -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_XMB -DHAVE_MATERIALUI -DHAVE_LIBRETRODB -DHAVE_CC_RESAMPLER DEFINES += -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DWANT_ZLIB DEFINES += -DHAVE_NETWORKING -DHAVE_CHEEVOS -DHAVE_SOCKET_LEGACY -DHAVE_THREADS + DEFINES += -DHAVE_CHD #-DHAVE_SSL -DMBEDTLS_SSL_DEBUG_ALL #ssl is currently incompatible with griffin due to use of the "static" flag on repeating functions that will conflict when included in one file else + HAVE_CHD = 1 HAVE_CC_RESAMPLER = 1 HAVE_MENU_COMMON = 1 HAVE_RTGA = 1 diff --git a/Makefile.emscripten b/Makefile.emscripten index ffb1f9e9fc..0a3c785311 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -18,6 +18,7 @@ HAVE_RPNG = 1 HAVE_EMSCRIPTEN = 1 HAVE_RGUI = 1 HAVE_SDL = 0 +HAVE_CHD = 1 HAVE_SDL2 = 0 HAVE_ZLIB = 1 WANT_ZLIB = 1 diff --git a/Makefile.vita b/Makefile.vita index 5645c9cabd..c2ed971153 100644 --- a/Makefile.vita +++ b/Makefile.vita @@ -18,11 +18,13 @@ ifeq ($(GRIFFIN_BUILD), 1) DEFINES += -DHAVE_GRIFFIN=1 DEFINES += -DHAVE_NEON -DHAVE_MENU -DHAVE_XMB -DHAVE_MATERIALUI -DHAVE_LIBRETRODB DEFINES -DHAVE_KEYMAPPER DEFINES += -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DWANT_ZLIB -DHAVE_CC_RESAMPLER + DEFINES += -DHAVE_CHD ifeq ($(DEBUG), 1) DEFINES += -DHAVE_NETLOGGER endif else + HAVE_CHD := 1 HAVE_NEON := 1 HAVE_FILTERS_BUILTIN := 1 HAVE_LANGEXTRA := 1 diff --git a/Makefile.wiiu b/Makefile.wiiu index b3f57e8918..1ecd6c5693 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -115,8 +115,10 @@ endif # DEFINES += -DWANT_IFADDRS # DEFINES += -DHAVE_FREETYPE DEFINES += -DHAVE_XMB -DHAVE_MATERIALUI + DEFINES += -DHAVE_CHD else HAVE_MENU_COMMON = 1 + HAVE_CHD = 1 HAVE_RTGA = 1 HAVE_RPNG = 1 HAVE_RJPEG = 1 From ca9dd899755eecd1830de41f9112f3c6005bc4d3 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 12:02:40 +0200 Subject: [PATCH 378/517] Revert "(3DS/Emscripten/Vita/Wiiu) Add HAVE_CHD support" This reverts commit d4c691527a844bcc15a166ce8077550f65448e4e. --- Makefile.ctr | 2 -- Makefile.emscripten | 1 - Makefile.vita | 2 -- Makefile.wiiu | 2 -- 4 files changed, 7 deletions(-) diff --git a/Makefile.ctr b/Makefile.ctr index 40b3cf160b..d47cc66c25 100644 --- a/Makefile.ctr +++ b/Makefile.ctr @@ -47,11 +47,9 @@ ifeq ($(GRIFFIN_BUILD), 1) DEFINES += -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_XMB -DHAVE_MATERIALUI -DHAVE_LIBRETRODB -DHAVE_CC_RESAMPLER DEFINES += -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DWANT_ZLIB DEFINES += -DHAVE_NETWORKING -DHAVE_CHEEVOS -DHAVE_SOCKET_LEGACY -DHAVE_THREADS - DEFINES += -DHAVE_CHD #-DHAVE_SSL -DMBEDTLS_SSL_DEBUG_ALL #ssl is currently incompatible with griffin due to use of the "static" flag on repeating functions that will conflict when included in one file else - HAVE_CHD = 1 HAVE_CC_RESAMPLER = 1 HAVE_MENU_COMMON = 1 HAVE_RTGA = 1 diff --git a/Makefile.emscripten b/Makefile.emscripten index 0a3c785311..ffb1f9e9fc 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -18,7 +18,6 @@ HAVE_RPNG = 1 HAVE_EMSCRIPTEN = 1 HAVE_RGUI = 1 HAVE_SDL = 0 -HAVE_CHD = 1 HAVE_SDL2 = 0 HAVE_ZLIB = 1 WANT_ZLIB = 1 diff --git a/Makefile.vita b/Makefile.vita index c2ed971153..5645c9cabd 100644 --- a/Makefile.vita +++ b/Makefile.vita @@ -18,13 +18,11 @@ ifeq ($(GRIFFIN_BUILD), 1) DEFINES += -DHAVE_GRIFFIN=1 DEFINES += -DHAVE_NEON -DHAVE_MENU -DHAVE_XMB -DHAVE_MATERIALUI -DHAVE_LIBRETRODB DEFINES -DHAVE_KEYMAPPER DEFINES += -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DWANT_ZLIB -DHAVE_CC_RESAMPLER - DEFINES += -DHAVE_CHD ifeq ($(DEBUG), 1) DEFINES += -DHAVE_NETLOGGER endif else - HAVE_CHD := 1 HAVE_NEON := 1 HAVE_FILTERS_BUILTIN := 1 HAVE_LANGEXTRA := 1 diff --git a/Makefile.wiiu b/Makefile.wiiu index 1ecd6c5693..b3f57e8918 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -115,10 +115,8 @@ endif # DEFINES += -DWANT_IFADDRS # DEFINES += -DHAVE_FREETYPE DEFINES += -DHAVE_XMB -DHAVE_MATERIALUI - DEFINES += -DHAVE_CHD else HAVE_MENU_COMMON = 1 - HAVE_CHD = 1 HAVE_RTGA = 1 HAVE_RPNG = 1 HAVE_RJPEG = 1 From db314ff82f1eb5f77afd12ac0405f05aed84e17a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 12:10:15 +0200 Subject: [PATCH 379/517] Uniquely namespace libchdr source files --- Makefile.common | 10 +++++----- griffin/griffin.c | 10 +++++----- .../libchdr/{bitstream.c => libchdr_bitstream.c} | 0 .../formats/libchdr/{cdrom.c => libchdr_cdrom.c} | 0 .../formats/libchdr/{chd.c => libchdr_chd.c} | 0 .../formats/libchdr/{flac.c => libchdr_flac.c} | 0 .../formats/libchdr/{huffman.c => libchdr_huffman.c} | 0 7 files changed, 10 insertions(+), 10 deletions(-) rename libretro-common/formats/libchdr/{bitstream.c => libchdr_bitstream.c} (100%) rename libretro-common/formats/libchdr/{cdrom.c => libchdr_cdrom.c} (100%) rename libretro-common/formats/libchdr/{chd.c => libchdr_chd.c} (100%) rename libretro-common/formats/libchdr/{flac.c => libchdr_flac.c} (100%) rename libretro-common/formats/libchdr/{huffman.c => libchdr_huffman.c} (100%) diff --git a/Makefile.common b/Makefile.common index 5de5f3d9b9..44e6b5652a 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1500,11 +1500,11 @@ ifeq ($(HAVE_7ZIP), 1) ifeq ($(HAVE_ZLIB), 1) DEFINES += -DHAVE_CHD -DWANT_SUBCODE -DWANT_RAW_DATA_SECTOR CFLAGS += -I$(LIBRETRO_COMM_DIR)/formats/libchdr - OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/bitstream.o \ - $(LIBRETRO_COMM_DIR)/formats/libchdr/cdrom.o \ - $(LIBRETRO_COMM_DIR)/formats/libchdr/chd.o \ - $(LIBRETRO_COMM_DIR)/formats/libchdr/flac.o \ - $(LIBRETRO_COMM_DIR)/formats/libchdr/huffman.o \ + OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_bitstream.o \ + $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_cdrom.o \ + $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_chd.o \ + $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_flac.o \ + $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_huffman.o \ $(LIBRETRO_COMM_DIR)/streams/chd_stream.o endif endif diff --git a/griffin/griffin.c b/griffin/griffin.c index d2b12c0674..e71cb8753f 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1307,11 +1307,11 @@ DEPENDENCIES #endif #ifdef HAVE_CHD -#include "../libretro-common/formats/libchdr/bitstream.c" -#include "../libretro-common/formats/libchdr/cdrom.c" -#include "../libretro-common/formats/libchdr/chd.c" -#include "../libretro-common/formats/libchdr/flac.c" -#include "../libretro-common/formats/libchdr/huffman.c" +#include "../libretro-common/formats/libchdr/libchdr_bitstream.c" +#include "../libretro-common/formats/libchdr/libchdr_cdrom.c" +#include "../libretro-common/formats/libchdr/libchdr_chd.c" +#include "../libretro-common/formats/libchdr/libchdr_flac.c" +#include "../libretro-common/formats/libchdr/libchdr_huffman.c" #include "../libretro-common/streams/chd_stream.c" #endif diff --git a/libretro-common/formats/libchdr/bitstream.c b/libretro-common/formats/libchdr/libchdr_bitstream.c similarity index 100% rename from libretro-common/formats/libchdr/bitstream.c rename to libretro-common/formats/libchdr/libchdr_bitstream.c diff --git a/libretro-common/formats/libchdr/cdrom.c b/libretro-common/formats/libchdr/libchdr_cdrom.c similarity index 100% rename from libretro-common/formats/libchdr/cdrom.c rename to libretro-common/formats/libchdr/libchdr_cdrom.c diff --git a/libretro-common/formats/libchdr/chd.c b/libretro-common/formats/libchdr/libchdr_chd.c similarity index 100% rename from libretro-common/formats/libchdr/chd.c rename to libretro-common/formats/libchdr/libchdr_chd.c diff --git a/libretro-common/formats/libchdr/flac.c b/libretro-common/formats/libchdr/libchdr_flac.c similarity index 100% rename from libretro-common/formats/libchdr/flac.c rename to libretro-common/formats/libchdr/libchdr_flac.c diff --git a/libretro-common/formats/libchdr/huffman.c b/libretro-common/formats/libchdr/libchdr_huffman.c similarity index 100% rename from libretro-common/formats/libchdr/huffman.c rename to libretro-common/formats/libchdr/libchdr_huffman.c From 4c941d3adad3f7aabea91f6cc0abecbcfbedd650 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 12:57:38 +0200 Subject: [PATCH 380/517] (gl2_renderchain) Try to prevent warning --- gfx/drivers_renderchain/gl2_renderchain.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/gfx/drivers_renderchain/gl2_renderchain.c b/gfx/drivers_renderchain/gl2_renderchain.c index eb819457de..0a72e89137 100644 --- a/gfx/drivers_renderchain/gl2_renderchain.c +++ b/gfx/drivers_renderchain/gl2_renderchain.c @@ -520,10 +520,8 @@ static void gl2_renderchain_deinit_fbo(void *data, if (chain) { - if (chain->fbo) - gl2_delete_fb(chain->fbo_pass, chain->fbo); - if (chain->fbo_texture) - glDeleteTextures(chain->fbo_pass, chain->fbo_texture); + gl2_delete_fb(chain->fbo_pass, chain->fbo); + glDeleteTextures(chain->fbo_pass, chain->fbo_texture); memset(chain->fbo_texture, 0, sizeof(chain->fbo_texture)); memset(chain->fbo, 0, sizeof(chain->fbo)); @@ -716,10 +714,9 @@ static void gl_create_fbo_texture(gl_t *gl, } } -static void gl_create_fbo_textures(gl_t *gl, void *chain_data) +static void gl_create_fbo_textures(gl_t *gl, gl2_renderchain_t *chain) { int i; - gl2_renderchain_t *chain = (gl2_renderchain_t*)chain_data; glGenTextures(chain->fbo_pass, chain->fbo_texture); From 3033dda866c1eabdd973e31d9c1451f0f40698b5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 13:05:40 +0200 Subject: [PATCH 381/517] (x11_common.c) Cleanups --- gfx/common/x11_common.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index e7b5172d6a..d8d3a353dd 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -430,7 +430,8 @@ static void x11_handle_key_event(XEvent *event, XIC ic, bool filter) status = 0; /* XwcLookupString doesn't seem to work. */ - num = Xutf8LookupString(ic, &event->xkey, keybuf, ARRAY_SIZE(keybuf), &keysym, &status); + num = Xutf8LookupString(ic, &event->xkey, keybuf, + ARRAY_SIZE(keybuf), &keysym, &status); /* libc functions need UTF-8 locale to work properly, * which makes mbrtowc a bit impractical. @@ -439,7 +440,8 @@ static void x11_handle_key_event(XEvent *event, XIC ic, bool filter) num = utf8_conv_utf32(chars, ARRAY_SIZE(chars), keybuf, num); #else (void)ic; - num = XLookupString(&event->xkey, keybuf, sizeof(keybuf), &keysym, NULL); /* ASCII only. */ + num = XLookupString(&event->xkey, keybuf, + sizeof(keybuf), &keysym, NULL); /* ASCII only. */ for (i = 0; i < num; i++) chars[i] = keybuf[i] & 0x7f; #endif @@ -720,12 +722,12 @@ static bool x11_check_atom_supported(Display *dpy, Atom atom) if (XA_NET_SUPPORTED == None) return false; - XGetWindowProperty(dpy, DefaultRootWindow(dpy), XA_NET_SUPPORTED, 0, UINT_MAX, False, XA_ATOM, &type, &format,&nitems, &bytes_after, (unsigned char **) &prop); + XGetWindowProperty(dpy, DefaultRootWindow(dpy), XA_NET_SUPPORTED, + 0, UINT_MAX, False, XA_ATOM, &type, &format,&nitems, + &bytes_after, (unsigned char **) &prop); if (!prop || type != XA_ATOM) - { return false; - } for (i = 0; i < nitems; i++) { @@ -799,9 +801,7 @@ char *x11_get_wm_name(Display *dpy) &propdata); if (status == Success && propdata) - { title = strdup((char *) propdata); - } else return NULL; From 58623d7540c4ba81ca136df8842656912c557f61 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 13:09:03 +0200 Subject: [PATCH 382/517] (d3d_common.h) Cleanups --- gfx/common/d3d_common.c | 6 +- gfx/common/d3d_common.h | 230 +--------------------------------------- 2 files changed, 9 insertions(+), 227 deletions(-) diff --git a/gfx/common/d3d_common.c b/gfx/common/d3d_common.c index 1908181945..5b5dc616a7 100644 --- a/gfx/common/d3d_common.c +++ b/gfx/common/d3d_common.c @@ -86,7 +86,8 @@ void *d3d_matrix_identity(void *_pout) return pout; } -void *d3d_matrix_ortho_off_center_lh(void *_pout, float l, float r, float b, float t, float zn, float zf) +void *d3d_matrix_ortho_off_center_lh(void *_pout, + float l, float r, float b, float t, float zn, float zf) { D3DMATRIX *pout = (D3DMATRIX*)_pout; @@ -101,7 +102,8 @@ void *d3d_matrix_ortho_off_center_lh(void *_pout, float l, float r, float b, flo return pout; } -void *d3d_matrix_multiply(void *_pout, const void *_pm1, const void *_pm2) +void *d3d_matrix_multiply(void *_pout, + const void *_pm1, const void *_pm2) { unsigned i,j; D3DMATRIX *pout = (D3DMATRIX*)_pout; diff --git a/gfx/common/d3d_common.h b/gfx/common/d3d_common.h index 083cfd7fa1..2899e3e8fa 100644 --- a/gfx/common/d3d_common.h +++ b/gfx/common/d3d_common.h @@ -41,241 +41,21 @@ typedef struct d3d_texture #define D3DTEXF_COMM_LINEAR 2 #define D3DPT_COMM_TRIANGLESTRIP 5 -#define D3D_COMM_CLEAR_TARGET 0x00000001l /* Clear target surface */ - -bool d3d_swap(void *data, void *dev); - -void *d3d_vertex_buffer_new(void *dev, - unsigned length, unsigned usage, unsigned fvf, - INT32 pool, void *handle); - -void *d3d_vertex_buffer_lock(void *data); -void d3d_vertex_buffer_unlock(void *data); - -void d3d_vertex_buffer_free(void *vertex_data, void *vertex_declaration); - -bool d3d_texture_get_level_desc(void *tex, - unsigned idx, void *_ppsurface_level); - -bool d3d_texture_get_surface_level(void *tex, - unsigned idx, void **_ppsurface_level); - -void *d3d_texture_new(void *dev, - const char *path, unsigned width, unsigned height, - unsigned miplevels, unsigned usage, INT32 format, - INT32 pool, unsigned filter, unsigned mipfilter, - INT32 color_key, void *src_info, - PALETTEENTRY *palette, bool want_mipmap); - -void d3d_set_stream_source(void *dev, unsigned stream_no, - void *stream_vertbuf, unsigned offset_bytes, - unsigned stride); - -void d3d_texture_free(void *tex); - -void d3d_set_transform(void *dev, - INT32 state, const void *_matrix); - -void d3d_set_sampler_address_u(void *dev, - unsigned sampler, unsigned value); - -void d3d_set_sampler_address_v(void *dev, - unsigned sampler, unsigned value); - -void d3d_set_sampler_minfilter(void *dev, - unsigned sampler, unsigned value); - -void d3d_set_sampler_magfilter(void *dev, - unsigned sampler, unsigned value); - -void d3d_set_sampler_mipfilter(void *dev, - unsigned sampler, unsigned value); - -bool d3d_begin_scene(void *dev); - -void d3d_end_scene(void *dev); - -void d3d_draw_primitive(void *dev, - INT32 type, unsigned start, unsigned count); - -void d3d_clear(void *dev, - unsigned count, const void *rects, unsigned flags, - INT32 color, float z, unsigned stencil); - -bool d3d_lock_rectangle(void *tex, - unsigned level, void *lock_rect, RECT *rect, - unsigned rectangle_height, unsigned flags); - -void d3d_lock_rectangle_clear(void *tex, - unsigned level, void *lock_rect, RECT *rect, - unsigned rectangle_height, unsigned flags); - -void d3d_unlock_rectangle(void *tex); - -void d3d_set_texture(void *dev, unsigned sampler, - void *tex_data); - -bool d3d_create_vertex_shader(void *dev, - const DWORD *a, void **b); - -bool d3d_create_pixel_shader(void *dev, - const DWORD *a, void **b); - -void d3d_free_pixel_shader(void *dev, void *data); - -void d3d_free_vertex_shader(void *dev, void *data); - -bool d3d_set_pixel_shader(void *dev, void *data); - -bool d3d_set_vertex_shader(void *dev, unsigned index, - void *data); - -bool d3d_set_vertex_shader_constantf(void *dev, - UINT start_register,const float* constant_data, unsigned vector4f_count); - -void d3d_texture_blit(unsigned pixel_size, - void *tex, - void *lr, const void *frame, - unsigned width, unsigned height, unsigned pitch); - -bool d3d_vertex_declaration_new(void *dev, - const void *vertex_data, void **decl_data); - -void d3d_vertex_declaration_free(void *data); - -void d3d_set_viewports(void *dev, void *vp); - -void d3d_enable_blend_func(void *data); - -void d3d_disable_blend_func(void *data); - -void d3d_set_vertex_declaration(void *data, void *vertex_data); - -void d3d_enable_alpha_blend_texture_func(void *data); - -void d3d_frame_postprocess(void *data); - -void d3d_surface_free(void *data); - -bool d3d_device_get_render_target_data(void *dev, - void *_src, void *_dst); - -bool d3d_device_get_render_target(void *dev, - unsigned idx, void **data); - -void d3d_device_set_render_target(void *dev, unsigned idx, - void *data); - -bool d3d_get_render_state(void *data, - INT32 state, DWORD *value); - -void d3d_set_render_state(void *data, - INT32 state, DWORD value); - -void d3d_device_set_render_target(void *dev, unsigned idx, - void *data); - -bool d3d_device_create_offscreen_plain_surface( - void *dev, - unsigned width, - unsigned height, - unsigned format, - unsigned pool, - void **surf_data, - void *data); - -bool d3d_surface_lock_rect(void *data, void *data2); - -void d3d_surface_unlock_rect(void *data); +/* Clear target surface */ +#define D3D_COMM_CLEAR_TARGET 0x00000001l void *d3d_matrix_transpose(void *_pout, const void *_pm); -void *d3d_matrix_multiply(void *_pout, - const void *_pm1, const void *_pm2); +void *d3d_matrix_identity(void *_pout); void *d3d_matrix_ortho_off_center_lh(void *_pout, float l, float r, float b, float t, float zn, float zf); -void * d3d_matrix_identity(void *_pout); +void *d3d_matrix_multiply(void *_pout, + const void *_pm1, const void *_pm2); void *d3d_matrix_rotation_z(void *_pout, float angle); -bool d3d_get_adapter_display_mode(void *d3d, - unsigned idx, - void *display_mode); - -bool d3d_create_device(void *dev, - void *d3dpp, - void *d3d, - HWND focus_window, - unsigned cur_mon_id); - -bool d3d_reset(void *dev, void *d3dpp); - -bool d3d_device_get_backbuffer(void *dev, - unsigned idx, unsigned swapchain_idx, - unsigned backbuffer_type, void **data); - -void d3d_device_free(void *dev, void *pd3d); - -void *d3d_create(void); - -bool d3d_initialize_symbols(enum gfx_ctx_api api); - -void d3d_deinitialize_symbols(void); - -bool d3d_check_device_type(void *d3d, - unsigned idx, - INT32 disp_format, - INT32 backbuffer_format, - bool windowed_mode); - -bool d3dx_create_font_indirect(void *dev, - void *desc, void **font_data); - -void d3dx_font_draw_text(void *data, void *sprite_data, void *string_data, - unsigned count, void *rect_data, unsigned format, unsigned color); - -void d3dx_font_get_text_metrics(void *data, void *metrics); - -void d3dxbuffer_release(void *data); - -void d3dx_font_release(void *data); - -INT32 d3d_translate_filter(unsigned type); - -bool d3dx_compile_shader( - const char *src, - unsigned src_data_len, - const void *pdefines, - void *pinclude, - const char *pfunctionname, - const char *pprofile, - unsigned flags, - void *ppshader, - void *pperrormsgs, - void *ppconstanttable); - -bool d3dx_compile_shader_from_file( - const char *src, - const void *pdefines, - void *pinclude, - const char *pfunctionname, - const char *pprofile, - unsigned flags, - void *ppshader, - void *pperrormsgs, - void *ppconstanttable); - -const void *d3dx_get_buffer_ptr(void *data); - -const bool d3dx_constant_table_set_float(void *p, - void *a, const void *b, float val); - -INT32 d3d_get_rgb565_format(void); -INT32 d3d_get_argb8888_format(void); -INT32 d3d_get_xrgb8888_format(void); - RETRO_END_DECLS #endif From 8de03fe843dbf052c09db2e23520b8ddc3737673 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 13:18:56 +0200 Subject: [PATCH 383/517] (D3D9 common) Define CINTERFACE and get rid of C++ ifdefs --- gfx/common/d3d9_common.c | 339 ++------------------------------------- 1 file changed, 11 insertions(+), 328 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index a6251e4b53..88f81729e4 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -13,6 +13,8 @@ * If not, see . */ +#define CINTERFACE + /* For Xbox we will just link statically * to Direct3D libraries instead. */ @@ -249,15 +251,6 @@ bool d3d9_check_device_type(void *_d3d, LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; if (!d3d) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (FAILED(d3d->CheckDeviceType( - 0, - D3DDEVTYPE_HAL, - (D3DFORMAT)disp_format, - (D3DFORMAT)backbuffer_format, - windowed_mode))) - return false; -#else if (FAILED(IDirect3D9_CheckDeviceType(d3d, 0, D3DDEVTYPE_HAL, @@ -265,7 +258,6 @@ bool d3d9_check_device_type(void *_d3d, (D3DFORMAT)backbuffer_format, windowed_mode))) return false; -#endif return true; } @@ -280,9 +272,6 @@ bool d3d9_get_adapter_display_mode( return false; #ifdef _XBOX return true; -#elif defined(__cplusplus) && !defined(CINTERFACE) - if (FAILED(d3d->GetAdapterDisplayMode(idx, (D3DDISPLAYMODE*)display_mode))) - return false; #else if (FAILED(IDirect3D9_GetAdapterDisplayMode(d3d, idx, (D3DDISPLAYMODE*)display_mode))) return false; @@ -294,21 +283,12 @@ bool d3d9_get_adapter_display_mode( bool d3d9_swap(void *data, void *_dev) { LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; -#if defined(__cplusplus) && !defined(CINTERFACE) -#ifdef _XBOX - dev->Present(NULL, NULL, NULL, NULL); -#else - if (dev->Present(NULL, NULL, NULL, NULL) != D3D_OK) - return false; -#endif -#else #ifdef _XBOX IDirect3DDevice9_Present(dev, NULL, NULL, NULL, NULL); #else if (IDirect3DDevice9_Present(dev, NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST) return false; -#endif #endif return true; } @@ -320,29 +300,20 @@ void d3d9_set_transform(void *_dev, CONST D3DMATRIX *matrix = (CONST D3DMATRIX*)_matrix; /* XBox 360 D3D9 does not support fixed-function pipeline. */ LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetTransform((D3DTRANSFORMSTATETYPE)state, matrix); -#else IDirect3DDevice9_SetTransform(dev, (D3DTRANSFORMSTATETYPE)state, matrix); #endif -#endif } bool d3d9_texture_get_level_desc(void *_tex, unsigned idx, void *_ppsurface_level) { LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (SUCCEEDED(tex->GetLevelDesc(idx, (D3DSURFACE_DESC*)_ppsurface_level))) - return true; -#else #if defined(_XBOX) D3DTexture_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level); return true; #else if (SUCCEEDED(IDirect3DTexture9_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level))) return true; -#endif #endif return false; @@ -354,13 +325,8 @@ bool d3d9_texture_get_surface_level(void *_tex, LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; if (!tex) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (SUCCEEDED(tex->GetSurfaceLevel(idx, (IDirect3DSurface9**)_ppsurface_level))) - return true; -#else if (SUCCEEDED(IDirect3DTexture9_GetSurfaceLevel(tex, idx, (IDirect3DSurface9**)_ppsurface_level))) return true; -#endif return false; } @@ -417,19 +383,11 @@ void *d3d9_texture_new(void *_dev, if (want_mipmap) usage |= D3DUSAGE_AUTOGENMIPMAP; #endif -#if defined(__cplusplus) && !defined(CINTERFACE) - hr = dev->CreateTexture( - width, height, miplevels, usage, - (D3DFORMAT)format, - (D3DPOOL)pool, - (struct IDirect3DTexture9**)&buf, NULL); -#else hr = IDirect3DDevice9_CreateTexture(dev, width, height, miplevels, usage, (D3DFORMAT)format, (D3DPOOL)pool, (struct IDirect3DTexture9**)&buf, NULL); -#endif } if (FAILED(hr)) @@ -443,11 +401,7 @@ void d3d9_texture_free(void *_tex) LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; if (!tex) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - tex->Release(); -#else IDirect3DTexture9_Release(tex); -#endif } bool d3d9_surface_lock_rect(void *data, void *data2) @@ -455,16 +409,11 @@ bool d3d9_surface_lock_rect(void *data, void *data2) LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; if (!surf) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (FAILED(surf->LockRect((D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY))) - return false; -#else #if defined(_XBOX) IDirect3DSurface9_LockRect(surf, (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY); #else if (FAILED(IDirect3DSurface9_LockRect(surf, (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY))) return false; -#endif #endif return true; @@ -475,11 +424,7 @@ void d3d9_surface_unlock_rect(void *data) LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; if (!surf) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - surf->UnlockRect(); -#else IDirect3DSurface9_UnlockRect(surf); -#endif } void d3d9_surface_free(void *data) @@ -487,11 +432,7 @@ void d3d9_surface_free(void *data) LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; if (!surf) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - surf->Release(); -#else IDirect3DSurface9_Release(surf); -#endif } void d3d9_vertex_declaration_free(void *data) @@ -499,16 +440,7 @@ void d3d9_vertex_declaration_free(void *data) if (!data) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - { - LPDIRECT3DVERTEXDECLARATION9 vertex_decl = - (LPDIRECT3DVERTEXDECLARATION9)data; - if (vertex_decl) - vertex_decl->Release(); - } -#else IDirect3DVertexDeclaration9_Release((LPDIRECT3DVERTEXDECLARATION9)data); -#endif } bool d3d9_vertex_declaration_new(void *_dev, @@ -518,15 +450,9 @@ bool d3d9_vertex_declaration_new(void *_dev, const D3DVERTEXELEMENT9 *vertex_elements = (const D3DVERTEXELEMENT9*)vertex_data; LPDIRECT3DVERTEXDECLARATION9 **vertex_decl = (LPDIRECT3DVERTEXDECLARATION9**)decl_data; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (SUCCEEDED(dev->CreateVertexDeclaration(vertex_elements, - (IDirect3DVertexDeclaration9**)vertex_decl))) - return true; -#else if (SUCCEEDED(IDirect3DDevice9_CreateVertexDeclaration(dev, vertex_elements, (IDirect3DVertexDeclaration9**)vertex_decl))) return true; -#endif return false; } @@ -539,28 +465,17 @@ void *d3d9_vertex_buffer_new(void *_dev, void *buf = NULL; LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; +#ifndef _XBOX if (usage == 0) { -#ifndef _XBOX -#if defined(__cplusplus) && !defined(CINTERFACE) - if (dev->GetSoftwareVertexProcessing()) - usage = D3DUSAGE_SOFTWAREPROCESSING; -#else if (IDirect3DDevice9_GetSoftwareVertexProcessing(dev)) usage = D3DUSAGE_SOFTWAREPROCESSING; -#endif -#endif } +#endif -#if defined(__cplusplus) && !defined(CINTERFACE) - hr = dev->CreateVertexBuffer(length, usage, fvf, - (D3DPOOL)pool, - (LPDIRECT3DVERTEXBUFFER9*)&buf, NULL); -#else hr = IDirect3DDevice9_CreateVertexBuffer(dev, length, usage, fvf, (D3DPOOL)pool, (LPDIRECT3DVERTEXBUFFER9*)&buf, NULL); -#endif if (FAILED(hr)) return NULL; @@ -574,11 +489,7 @@ void d3d9_vertex_buffer_unlock(void *vertbuf_ptr) if (!vertbuf) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - vertbuf->Unlock(); -#else IDirect3DVertexBuffer9_Unlock(vertbuf); -#endif } void *d3d9_vertex_buffer_lock(void *vertbuf_ptr) @@ -587,11 +498,7 @@ void *d3d9_vertex_buffer_lock(void *vertbuf_ptr) LPDIRECT3DVERTEXBUFFER9 vertbuf = (LPDIRECT3DVERTEXBUFFER9)vertbuf_ptr; if (!vertbuf) return NULL; -#if defined(__cplusplus) && !defined(CINTERFACE) - vertbuf->Lock(0, 0, &buf, 0); -#else IDirect3DVertexBuffer9_Lock(vertbuf, 0, 0, &buf, 0); -#endif if (!buf) return NULL; @@ -604,11 +511,7 @@ void d3d9_vertex_buffer_free(void *vertex_data, void *vertex_declaration) if (vertex_data) { LPDIRECT3DVERTEXBUFFER9 buf = (LPDIRECT3DVERTEXBUFFER9)vertex_data; -#if defined(__cplusplus) && !defined(CINTERFACE) - buf->Release(); -#else IDirect3DVertexBuffer9_Release(buf); -#endif buf = NULL; } @@ -628,13 +531,9 @@ void d3d9_set_stream_source(void *_dev, unsigned stream_no, LPDIRECT3DVERTEXBUFFER9 stream_vertbuf = (LPDIRECT3DVERTEXBUFFER9)stream_vertbuf_ptr; if (!stream_vertbuf) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetStreamSource(stream_no, stream_vertbuf, offset_bytes, stride); -#else IDirect3DDevice9_SetStreamSource(dev, stream_no, stream_vertbuf, offset_bytes, stride); -#endif } bool d3d9_device_create_offscreen_plain_surface( @@ -648,20 +547,12 @@ bool d3d9_device_create_offscreen_plain_surface( { #ifndef _XBOX LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (SUCCEEDED(dev->CreateOffscreenPlainSurface(width, height, - (D3DFORMAT)format, (D3DPOOL)pool, - (LPDIRECT3DSURFACE9*)surf_data, - (HANDLE*)data))) - return true; -#else if (SUCCEEDED(IDirect3DDevice9_CreateOffscreenPlainSurface(dev, width, height, (D3DFORMAT)format, (D3DPOOL)pool, (LPDIRECT3DSURFACE9*)surf_data, (HANDLE*)data))) return true; -#endif #endif return false; @@ -673,36 +564,23 @@ static void d3d9_set_texture_stage_state(void *_dev, #ifndef _XBOX /* XBox 360 has no fixed-function pipeline. */ LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (dev->SetTextureStageState(sampler, (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) - RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); -#else if (IDirect3DDevice9_SetTextureStageState(dev, sampler, (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); #endif -#endif } void d3d9_set_sampler_address_u(void *_dev, unsigned sampler, unsigned value) { LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetSamplerState(sampler, D3DSAMP_ADDRESSU, value); -#else IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_ADDRESSU, value); -#endif } void d3d9_set_sampler_address_v(void *_dev, unsigned sampler, unsigned value) { LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetSamplerState(sampler, D3DSAMP_ADDRESSV, value); -#else IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_ADDRESSV, value); -#endif } void d3d9_set_sampler_minfilter(void *_dev, @@ -711,11 +589,7 @@ void d3d9_set_sampler_minfilter(void *_dev, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetSamplerState(sampler, D3DSAMP_MINFILTER, value); -#else IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_MINFILTER, value); -#endif } void d3d9_set_sampler_magfilter(void *_dev, @@ -724,11 +598,7 @@ void d3d9_set_sampler_magfilter(void *_dev, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetSamplerState(sampler, D3DSAMP_MAGFILTER, value); -#else IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_MAGFILTER, value); -#endif } void d3d9_set_sampler_mipfilter(void *_dev, @@ -746,16 +616,11 @@ bool d3d9_begin_scene(void *_dev) LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (FAILED(dev->BeginScene())) - return false; -#else #if defined(_XBOX) IDirect3DDevice9_BeginScene(dev); #else if (FAILED(IDirect3DDevice9_BeginScene(dev))) return false; -#endif #endif return true; @@ -766,11 +631,7 @@ void d3d9_end_scene(void *_dev) LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->EndScene(); -#else IDirect3DDevice9_EndScene(dev); -#endif } static void d3d9_draw_primitive_internal(void *_dev, @@ -779,11 +640,7 @@ static void d3d9_draw_primitive_internal(void *_dev, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->DrawPrimitive(type, start, count); -#else IDirect3DDevice9_DrawPrimitive(dev, type, start, count); -#endif } void d3d9_draw_primitive(void *dev, @@ -803,12 +660,8 @@ void d3d9_clear(void *_dev, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->Clear(count, (const D3DRECT*)rects, flags, color, z, stencil); -#else IDirect3DDevice9_Clear(dev, count, (const D3DRECT*)rects, flags, color, z, stencil); -#endif } bool d3d9_device_get_render_target_data(void *_dev, @@ -820,14 +673,9 @@ bool d3d9_device_get_render_target_data(void *_dev, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (SUCCEEDED(dev->GetRenderTargetData(src, dst))) - return true; -#else if (SUCCEEDED(IDirect3DDevice9_GetRenderTargetData( dev, src, dst))) return true; -#endif #endif return false; @@ -839,15 +687,9 @@ bool d3d9_device_get_render_target(void *_dev, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (SUCCEEDED(dev->GetRenderTarget(idx, - (LPDIRECT3DSURFACE9*)data))) - return true; -#else if (SUCCEEDED(IDirect3DDevice9_GetRenderTarget(dev, idx, (LPDIRECT3DSURFACE9*)data))) return true; -#endif return false; } @@ -861,16 +703,11 @@ bool d3d9_lock_rectangle(void *_tex, LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; if (!tex) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (FAILED(tex->LockRect(level, lr, rect, flags))) - return false; -#else #ifdef _XBOX IDirect3DTexture9_LockRect(tex, level, lr, (const RECT*)rect, flags); #else if (IDirect3DTexture9_LockRect(tex, level, lr, (const RECT*)rect, flags) != D3D_OK) return false; -#endif #endif return true; @@ -881,11 +718,7 @@ void d3d9_unlock_rectangle(void *_tex) LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; if (!tex) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - tex->UnlockRect(0); -#else IDirect3DTexture9_UnlockRect(tex, 0); -#endif } void d3d9_lock_rectangle_clear(void *tex, @@ -906,11 +739,7 @@ void d3d9_set_viewports(void *_dev, void *_vp) D3DVIEWPORT9 *vp = (D3DVIEWPORT9*)_vp; if (!dev) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetViewport(vp); -#else IDirect3DDevice9_SetViewport(dev, vp); -#endif } void d3d9_set_texture(void *_dev, unsigned sampler, @@ -920,12 +749,8 @@ void d3d9_set_texture(void *_dev, unsigned sampler, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev || !tex) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetTexture(sampler, tex); -#else IDirect3DDevice9_SetTexture(dev, sampler, (IDirect3DBaseTexture9*)tex); -#endif } void d3d9_free_vertex_shader(void *_dev, void *data) @@ -934,11 +759,7 @@ void d3d9_free_vertex_shader(void *_dev, void *data) IDirect3DVertexShader9 *vs = (IDirect3DVertexShader9*)data; if (!dev || !vs) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - vs->Release(); -#else IDirect3DVertexShader9_Release(vs); -#endif } void d3d9_free_pixel_shader(void *_dev, void *data) @@ -947,11 +768,7 @@ void d3d9_free_pixel_shader(void *_dev, void *data) IDirect3DPixelShader9 *ps = (IDirect3DPixelShader9*)data; if (!dev || !ps) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - ps->Release(); -#else IDirect3DPixelShader9_Release(ps); -#endif } bool d3d9_create_vertex_shader(void *_dev, const DWORD *a, void **b) @@ -960,14 +777,9 @@ bool d3d9_create_vertex_shader(void *_dev, const DWORD *a, void **b) if (!dev) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (dev->CreateVertexShader(a, (IDirect3DVertexShader9**)b) == D3D_OK) - return true; -#else if (IDirect3DDevice9_CreateVertexShader(dev, a, (LPDIRECT3DVERTEXSHADER9*)b) == D3D_OK) return true; -#endif return false; } @@ -978,14 +790,9 @@ bool d3d9_create_pixel_shader(void *_dev, const DWORD *a, void **b) if (!dev) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (dev->CreatePixelShader(a, (IDirect3DPixelShader9**)b) == D3D_OK) - return true; -#else if (IDirect3DDevice9_CreatePixelShader(dev, a, (LPDIRECT3DPIXELSHADER9*)b) == D3D_OK) return true; -#endif return false; } @@ -996,10 +803,7 @@ bool d3d9_set_pixel_shader(void *_dev, void *data) LPDIRECT3DPIXELSHADER9 d3dps = (LPDIRECT3DPIXELSHADER9)data; if (!dev || !d3dps) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (dev->SetPixelShader(d3dps) == D3D_OK) - return true; -#else + #ifdef _XBOX /* Returns void on Xbox */ IDirect3DDevice9_SetPixelShader(dev, d3dps); @@ -1007,7 +811,6 @@ bool d3d9_set_pixel_shader(void *_dev, void *data) #else if (IDirect3DDevice9_SetPixelShader(dev, d3dps) == D3D_OK) return true; -#endif #endif return false; @@ -1018,16 +821,12 @@ bool d3d9_set_vertex_shader(void *_dev, unsigned index, { LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; LPDIRECT3DVERTEXSHADER9 shader = (LPDIRECT3DVERTEXSHADER9)data; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (dev->SetVertexShader(shader) != D3D_OK) - return false; -#else + #ifdef _XBOX IDirect3DDevice9_SetVertexShader(dev, shader); #else if (IDirect3DDevice9_SetVertexShader(dev, shader) != D3D_OK) return false; -#endif #endif return true; @@ -1038,16 +837,7 @@ bool d3d9_set_vertex_shader_constantf(void *_dev, unsigned vector4f_count) { LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; -#if defined(__cplusplus) && !defined(CINTERFACE) -#ifdef _XBOX - dev->SetVertexShaderConstantF( - start_register, constant_data, vector4f_count); -#else - if (dev->SetVertexShaderConstantF( - start_register, constant_data, vector4f_count) == D3D_OK) - return true; -#endif -#else + #ifdef _XBOX IDirect3DDevice9_SetVertexShaderConstantF(dev, start_register, constant_data, vector4f_count); @@ -1056,7 +846,6 @@ bool d3d9_set_vertex_shader_constantf(void *_dev, if (IDirect3DDevice9_SetVertexShaderConstantF(dev, start_register, constant_data, vector4f_count) == D3D_OK) return true; -#endif #endif return false; @@ -1083,20 +872,15 @@ bool d3d9_get_render_state(void *data, INT32 state, DWORD *value) LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; if (!dev) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (dev->GetRenderState((D3DRENDERSTATETYPE)state, value) == D3D_OK) - return true; -#else + #ifdef _XBOX IDirect3DDevice9_GetRenderState(dev, (D3DRENDERSTATETYPE)state, value); return true; #else if (IDirect3DDevice9_GetRenderState(dev, (D3DRENDERSTATETYPE)state, value) == D3D_OK) return true; -#endif -#endif - return false; +#endif } void d3d9_set_render_state(void *data, INT32 state, DWORD value) @@ -1104,11 +888,7 @@ void d3d9_set_render_state(void *data, INT32 state, DWORD value) LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; if (!dev) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetRenderState((D3DRENDERSTATETYPE)state, value); -#else IDirect3DDevice9_SetRenderState(dev, (D3DRENDERSTATETYPE)state, value); -#endif } void d3d9_enable_blend_func(void *data) @@ -1128,11 +908,7 @@ void d3d9_device_set_render_target(void *_dev, unsigned idx, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetRenderTarget(idx, surf); -#else IDirect3DDevice9_SetRenderTarget(dev, idx, surf); -#endif } void d3d9_enable_alpha_blend_texture_func(void *data) @@ -1153,11 +929,7 @@ void d3d9_set_vertex_declaration(void *data, void *vertex_data) LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; if (!dev) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->SetVertexDeclaration((LPDIRECT3DVERTEXDECLARATION9)vertex_data); -#else IDirect3DDevice9_SetVertexDeclaration(dev, (LPDIRECT3DVERTEXDECLARATION9)vertex_data); -#endif } static bool d3d9_reset_internal(void *data, @@ -1167,13 +939,8 @@ static bool d3d9_reset_internal(void *data, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; if (!dev) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if ((dev->Reset(d3dpp) == D3D_OK)) - return true; -#else if (IDirect3DDevice9_Reset(dev, d3dpp) == D3D_OK) return true; -#endif return false; } @@ -1186,12 +953,8 @@ static HRESULT d3d9_test_cooperative_level(void *data) LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; if (!dev) return E_FAIL; -#if defined(__cplusplus) && !defined(CINTERFACE) - return dev->TestCooperativeLevel(); -#else return IDirect3DDevice9_TestCooperativeLevel(dev); #endif -#endif } static bool d3d9_create_device_internal( @@ -1206,16 +969,6 @@ static bool d3d9_create_device_internal( LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; if (!dev) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (SUCCEEDED(d3d->CreateDevice( - cur_mon_id, - D3DDEVTYPE_HAL, - focus_window, - behavior_flags, - d3dpp, - (IDirect3DDevice9**)dev))) - return true; -#else if (SUCCEEDED(IDirect3D9_CreateDevice(d3d, cur_mon_id, D3DDEVTYPE_HAL, @@ -1224,7 +977,6 @@ static bool d3d9_create_device_internal( d3dpp, (IDirect3DDevice9**)dev))) return true; -#endif return false; } @@ -1291,19 +1043,11 @@ bool d3d9_device_get_backbuffer(void *_dev, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (SUCCEEDED(dev->GetBackBuffer( - swapchain_idx, idx, - (D3DBACKBUFFER_TYPE)backbuffer_type, - (LPDIRECT3DSURFACE9*)data))) - return true; -#else if (SUCCEEDED(IDirect3DDevice9_GetBackBuffer(dev, swapchain_idx, idx, (D3DBACKBUFFER_TYPE)backbuffer_type, (LPDIRECT3DSURFACE9*)data))) return true; -#endif return false; } @@ -1314,22 +1058,10 @@ void d3d9_device_free(void *_dev, void *_pd3d) LPDIRECT3D9 pd3d = (LPDIRECT3D9)_pd3d; LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev) - { -#if defined(__cplusplus) && !defined(CINTERFACE) - dev->Release(); -#else IDirect3DDevice9_Release(dev); -#endif - } if (pd3d) - { -#if defined(__cplusplus) && !defined(CINTERFACE) - pd3d->Release(); -#else IDirect3D9_Release(pd3d); -#endif - } } INT32 d3d9_translate_filter(unsigned type) @@ -1357,17 +1089,10 @@ bool d3d9x_create_font_indirect(void *_dev, { #ifdef HAVE_D3DX LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; -#ifdef __cplusplus if (SUCCEEDED(D3D9CreateFontIndirect( dev, (D3DXFONT_DESC*)desc, (struct ID3DXFont**)font_data))) return true; -#else - if (SUCCEEDED(D3D9CreateFontIndirect( - dev, (D3DXFONT_DESC*)desc, - (struct ID3DXFont**)font_data))) - return true; -#endif #endif return false; @@ -1376,20 +1101,12 @@ bool d3d9x_create_font_indirect(void *_dev, void d3dxbuffer_release(void *data) { #ifdef HAVE_D3DX -#ifdef __cplusplus - ID3DXBuffer *p = (ID3DXBuffer*)data; -#else LPD3DXBUFFER p = (LPD3DXBUFFER)data; -#endif if (!p) return; -#if defined(__cplusplus) && !defined(CINTERFACE) - p->Release(); -#else p->lpVtbl->Release(p); #endif -#endif } bool d3d9x_compile_shader( @@ -1426,55 +1143,32 @@ void d3d9x_font_draw_text(void *data, void *sprite_data, void *string_data, unsigned count, void *rect_data, unsigned format, unsigned color) { #ifdef HAVE_D3DX -#if !defined(__cplusplus) || defined(CINTERFACE) ID3DXFont *font = (ID3DXFont*)data; if (!font) return; font->lpVtbl->DrawText(font, (LPD3DXSPRITE)sprite_data, (LPCTSTR)string_data, count, (LPRECT)rect_data, (DWORD)format, (D3DCOLOR)color); -#else - LPD3DXFONT font = (LPD3DXFONT)data; - if (!font) - return; - font->DrawText((LPD3DXSPRITE)sprite_data, - (LPCTSTR)string_data, count, (LPRECT)rect_data, - (DWORD)format, (D3DCOLOR)color); -#endif #endif } void d3d9x_font_release(void *data) { #ifdef HAVE_D3DX -#if !defined(__cplusplus) || defined(CINTERFACE) ID3DXFont *font = (ID3DXFont*)data; if (!font) return; font->lpVtbl->Release(font); -#else - LPD3DXFONT font = (LPD3DXFONT)data; - if (!font) - return; - font->Release(); -#endif #endif } void d3d9x_font_get_text_metrics(void *data, void *metrics) { #ifdef HAVE_D3DX -#if !defined(__cplusplus) || defined(CINTERFACE) ID3DXFont *font = (ID3DXFont*)data; if (!font) return; font->lpVtbl->GetTextMetrics(font, (TEXTMETRICA*)metrics); -#else - LPD3DXFONT font = (LPD3DXFONT)data; - if (!font) - return; - font->GetTextMetricsA((TEXTMETRICA*)metrics); -#endif #endif } @@ -1537,16 +1231,10 @@ const void *d3d9x_get_buffer_ptr(void *data) { #if defined(HAVE_D3DX) ID3DXBuffer *listing = (ID3DXBuffer*)data; - if (!listing) - return NULL; -#if defined(__cplusplus) && !defined(CINTERFACE) - return listing->GetBufferPointer(); -#else - return listing->lpVtbl->GetBufferPointer(listing); + if (listing) + return listing->lpVtbl->GetBufferPointer(listing); #endif -#else return NULL; -#endif } const bool d3d9x_constant_table_set_float(void *p, @@ -1559,13 +1247,8 @@ const bool d3d9x_constant_table_set_float(void *p, LPD3DXCONSTANTTABLE consttbl = (LPD3DXCONSTANTTABLE)p; if (!consttbl || !dev || !handle) return false; -#if defined(__cplusplus) && !defined(CINTERFACE) - if (consttbl->SetFloat(dev, handle, val) == D3D_OK) - return true; -#else if (consttbl->lpVtbl->SetFloat(consttbl, dev, handle, val) == D3D_OK) return true; -#endif #endif return false; } From 5ee6cdf753bf76051903138e0778cad5db6bfbe9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 13:26:58 +0200 Subject: [PATCH 384/517] (d3d9_common.c) Cleanups --- gfx/common/d3d9_common.c | 125 ++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 75 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index 88f81729e4..73b3134684 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -249,17 +249,16 @@ bool d3d9_check_device_type(void *_d3d, bool windowed_mode) { LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; - if (!d3d) - return false; - if (FAILED(IDirect3D9_CheckDeviceType(d3d, + if (d3d && + SUCCEEDED(IDirect3D9_CheckDeviceType(d3d, 0, D3DDEVTYPE_HAL, (D3DFORMAT)disp_format, (D3DFORMAT)backbuffer_format, windowed_mode))) - return false; + return true; - return true; + return false; } bool d3d9_get_adapter_display_mode( @@ -270,9 +269,7 @@ bool d3d9_get_adapter_display_mode( LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; if (!d3d) return false; -#ifdef _XBOX - return true; -#else +#ifndef _XBOX if (FAILED(IDirect3D9_GetAdapterDisplayMode(d3d, idx, (D3DDISPLAYMODE*)display_mode))) return false; #endif @@ -310,22 +307,20 @@ bool d3d9_texture_get_level_desc(void *_tex, LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; #if defined(_XBOX) D3DTexture_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level); - return true; #else - if (SUCCEEDED(IDirect3DTexture9_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level))) - return true; + if (FAILED(IDirect3DTexture9_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level))) + return false; #endif - - return false; + return true; } bool d3d9_texture_get_surface_level(void *_tex, unsigned idx, void **_ppsurface_level) { LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; - if (!tex) - return false; - if (SUCCEEDED(IDirect3DTexture9_GetSurfaceLevel(tex, idx, (IDirect3DSurface9**)_ppsurface_level))) + if (tex && + SUCCEEDED(IDirect3DTexture9_GetSurfaceLevel( + tex, idx, (IDirect3DSurface9**)_ppsurface_level))) return true; return false; @@ -361,7 +356,7 @@ void *d3d9_texture_new(void *_dev, INT32 color_key, void *src_info_data, PALETTEENTRY *palette, bool want_mipmap) { - HRESULT hr = S_OK; + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; void *buf = NULL; if (path) @@ -377,20 +372,15 @@ void *d3d9_texture_new(void *_dev, #endif } - { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; #ifndef _XBOX - if (want_mipmap) - usage |= D3DUSAGE_AUTOGENMIPMAP; + if (want_mipmap) + usage |= D3DUSAGE_AUTOGENMIPMAP; #endif - hr = IDirect3DDevice9_CreateTexture(dev, - width, height, miplevels, usage, - (D3DFORMAT)format, - (D3DPOOL)pool, - (struct IDirect3DTexture9**)&buf, NULL); - } - - if (FAILED(hr)) + if (FAILED(IDirect3DDevice9_CreateTexture(dev, + width, height, miplevels, usage, + (D3DFORMAT)format, + (D3DPOOL)pool, + (struct IDirect3DTexture9**)&buf, NULL))) return NULL; return buf; @@ -412,7 +402,8 @@ bool d3d9_surface_lock_rect(void *data, void *data2) #if defined(_XBOX) IDirect3DSurface9_LockRect(surf, (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY); #else - if (FAILED(IDirect3DSurface9_LockRect(surf, (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY))) + if (FAILED(IDirect3DSurface9_LockRect(surf, + (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY))) return false; #endif @@ -461,23 +452,18 @@ void *d3d9_vertex_buffer_new(void *_dev, unsigned length, unsigned usage, unsigned fvf, INT32 pool, void *handle) { - HRESULT hr = S_OK; void *buf = NULL; LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; #ifndef _XBOX if (usage == 0) - { if (IDirect3DDevice9_GetSoftwareVertexProcessing(dev)) usage = D3DUSAGE_SOFTWAREPROCESSING; - } #endif - hr = IDirect3DDevice9_CreateVertexBuffer(dev, length, usage, fvf, + if (FAILED(IDirect3DDevice9_CreateVertexBuffer(dev, length, usage, fvf, (D3DPOOL)pool, - (LPDIRECT3DVERTEXBUFFER9*)&buf, NULL); - - if (FAILED(hr)) + (LPDIRECT3DVERTEXBUFFER9*)&buf, NULL))) return NULL; return buf; @@ -671,9 +657,8 @@ bool d3d9_device_get_render_target_data(void *_dev, LPDIRECT3DSURFACE9 src = (LPDIRECT3DSURFACE9)_src; LPDIRECT3DSURFACE9 dst = (LPDIRECT3DSURFACE9)_dst; LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return false; - if (SUCCEEDED(IDirect3DDevice9_GetRenderTargetData( + if (dev && + SUCCEEDED(IDirect3DDevice9_GetRenderTargetData( dev, src, dst))) return true; #endif @@ -685,9 +670,8 @@ bool d3d9_device_get_render_target(void *_dev, unsigned idx, void **data) { LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return false; - if (SUCCEEDED(IDirect3DDevice9_GetRenderTarget(dev, + if (dev && + SUCCEEDED(IDirect3DDevice9_GetRenderTarget(dev, idx, (LPDIRECT3DSURFACE9*)data))) return true; @@ -727,7 +711,7 @@ void d3d9_lock_rectangle_clear(void *tex, { D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; #if defined(_XBOX) - level = 0; + level = 0; #endif memset(lr->pBits, level, rectangle_height * lr->Pitch); d3d9_unlock_rectangle(tex); @@ -787,10 +771,9 @@ bool d3d9_create_vertex_shader(void *_dev, const DWORD *a, void **b) bool d3d9_create_pixel_shader(void *_dev, const DWORD *a, void **b) { LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return false; - if (IDirect3DDevice9_CreatePixelShader(dev, a, + if (dev && + IDirect3DDevice9_CreatePixelShader(dev, a, (LPDIRECT3DPIXELSHADER9*)b) == D3D_OK) return true; @@ -807,13 +790,11 @@ bool d3d9_set_pixel_shader(void *_dev, void *data) #ifdef _XBOX /* Returns void on Xbox */ IDirect3DDevice9_SetPixelShader(dev, d3dps); - return true; #else - if (IDirect3DDevice9_SetPixelShader(dev, d3dps) == D3D_OK) - return true; + if (IDirect3DDevice9_SetPixelShader(dev, d3dps) != D3D_OK) + return false; #endif - - return false; + return true; } bool d3d9_set_vertex_shader(void *_dev, unsigned index, @@ -841,14 +822,13 @@ bool d3d9_set_vertex_shader_constantf(void *_dev, #ifdef _XBOX IDirect3DDevice9_SetVertexShaderConstantF(dev, start_register, constant_data, vector4f_count); - return true; #else if (IDirect3DDevice9_SetVertexShaderConstantF(dev, - start_register, constant_data, vector4f_count) == D3D_OK) - return true; + start_register, constant_data, vector4f_count) != D3D_OK) + return false; #endif - return false; + return true; } void d3d9_texture_blit(unsigned pixel_size, @@ -875,12 +855,11 @@ bool d3d9_get_render_state(void *data, INT32 state, DWORD *value) #ifdef _XBOX IDirect3DDevice9_GetRenderState(dev, (D3DRENDERSTATETYPE)state, value); - return true; #else - if (IDirect3DDevice9_GetRenderState(dev, (D3DRENDERSTATETYPE)state, value) == D3D_OK) - return true; - return false; + if (IDirect3DDevice9_GetRenderState(dev, (D3DRENDERSTATETYPE)state, value) != D3D_OK) + return false; #endif + return true; } void d3d9_set_render_state(void *data, INT32 state, DWORD value) @@ -937,9 +916,8 @@ static bool d3d9_reset_internal(void *data, ) { LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; - if (!dev) - return false; - if (IDirect3DDevice9_Reset(dev, d3dpp) == D3D_OK) + if (dev && + IDirect3DDevice9_Reset(dev, d3dpp) == D3D_OK) return true; return false; @@ -947,14 +925,12 @@ static bool d3d9_reset_internal(void *data, static HRESULT d3d9_test_cooperative_level(void *data) { -#ifdef _XBOX - return E_FAIL; -#else +#ifndef _XBOX LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; - if (!dev) - return E_FAIL; - return IDirect3DDevice9_TestCooperativeLevel(dev); + if (dev) + return IDirect3DDevice9_TestCooperativeLevel(dev); #endif + return E_FAIL; } static bool d3d9_create_device_internal( @@ -967,9 +943,8 @@ static bool d3d9_create_device_internal( { LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; - if (!dev) - return false; - if (SUCCEEDED(IDirect3D9_CreateDevice(d3d, + if (dev && + SUCCEEDED(IDirect3D9_CreateDevice(d3d, cur_mon_id, D3DDEVTYPE_HAL, focus_window, @@ -1245,9 +1220,9 @@ const bool d3d9x_constant_table_set_float(void *p, LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)a; D3DXHANDLE handle = (D3DXHANDLE)b; LPD3DXCONSTANTTABLE consttbl = (LPD3DXCONSTANTTABLE)p; - if (!consttbl || !dev || !handle) - return false; - if (consttbl->lpVtbl->SetFloat(consttbl, dev, handle, val) == D3D_OK) + if (consttbl && dev && handle && + consttbl->lpVtbl->SetFloat( + consttbl, dev, handle, val) == D3D_OK) return true; #endif return false; From a7e40ea3f4896692c185303d10eba44926eefa4b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 13:34:05 +0200 Subject: [PATCH 385/517] (d3d8_common.c) Define CINTERFACE, get rid of C++ versions --- gfx/common/d3d8_common.c | 334 ++++++--------------------------------- 1 file changed, 51 insertions(+), 283 deletions(-) diff --git a/gfx/common/d3d8_common.c b/gfx/common/d3d8_common.c index 07fb3cf1a0..d2c8507ffb 100644 --- a/gfx/common/d3d8_common.c +++ b/gfx/common/d3d8_common.c @@ -13,6 +13,8 @@ * If not, see . */ +#define CINTERFACE + /* For Xbox we will just link statically * to Direct3D libraries instead. */ @@ -179,27 +181,16 @@ bool d3d8_check_device_type(void *_d3d, bool windowed_mode) { LPDIRECT3D8 d3d = (LPDIRECT3D8)_d3d; - if (!d3d) - return false; -#ifdef __cplusplus - if (FAILED(d3d->CheckDeviceType( - 0, - D3DDEVTYPE_HAL, - (D3DFORMAT)disp_format, - (D3DFORMAT)backbuffer_format, - windowed_mode))) - return false; -#else - if (FAILED(IDirect3D8_CheckDeviceType(d3d, + if (d3d && + SUCCEEDED(IDirect3D8_CheckDeviceType(d3d, 0, D3DDEVTYPE_HAL, disp_format, backbuffer_format, windowed_mode))) - return false; -#endif + return true; - return true; + return false; } bool d3d8_get_adapter_display_mode( @@ -208,30 +199,20 @@ bool d3d8_get_adapter_display_mode( void *display_mode) { LPDIRECT3D8 d3d = (LPDIRECT3D8)_d3d; - if (!d3d) - return false; -#ifdef __cplusplus - if (FAILED(d3d->GetAdapterDisplayMode(idx, (D3DDISPLAYMODE*)display_mode))) - return false; -#else - if (FAILED(IDirect3D8_GetAdapterDisplayMode(d3d, idx, (D3DDISPLAYMODE*)display_mode))) - return false; -#endif + if (d3d && + SUCCEEDED(IDirect3D8_GetAdapterDisplayMode( + d3d, idx, (D3DDISPLAYMODE*)display_mode))) + return true; - return true; + return false; } bool d3d8_swap(void *data, void *_dev) { LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; -#ifdef __cplusplus - if (dev->Present(NULL, NULL, NULL, NULL) != D3D_OK) - return false; -#else if (IDirect3DDevice8_Present(dev, NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST) return false; -#endif return true; } @@ -240,24 +221,15 @@ void d3d8_set_transform(void *_dev, { CONST D3DMATRIX *matrix = (CONST D3DMATRIX*)_matrix; LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; -#ifdef __cplusplus - dev->SetTransform((D3DTRANSFORMSTATETYPE)state, matrix); -#else IDirect3DDevice8_SetTransform(dev, (D3DTRANSFORMSTATETYPE)state, matrix); -#endif } bool d3d8_texture_get_level_desc(void *_tex, unsigned idx, void *_ppsurface_level) { LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; -#ifdef __cplusplus - if (SUCCEEDED(tex->GetLevelDesc(idx, (D3DSURFACE_DESC*)_ppsurface_level))) - return true; -#else if (SUCCEEDED(IDirect3DTexture8_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level))) return true; -#endif return false; } @@ -266,16 +238,11 @@ bool d3d8_texture_get_surface_level(void *_tex, unsigned idx, void **_ppsurface_level) { LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; - if (!tex) - return false; -#ifdef __cplusplus - if (SUCCEEDED(tex->GetSurfaceLevel(idx, (IDirect3DSurface8**)_ppsurface_level))) + if (tex && + SUCCEEDED( + IDirect3DTexture8_GetSurfaceLevel( + tex, idx, (IDirect3DSurface8**)_ppsurface_level))) return true; -#else - if (SUCCEEDED(IDirect3DTexture8_GetSurfaceLevel(tex, idx, (IDirect3DSurface8**)_ppsurface_level))) - return true; -#endif - return false; } @@ -308,7 +275,7 @@ void *d3d8_texture_new(void *_dev, INT32 color_key, void *src_info_data, PALETTEENTRY *palette, bool want_mipmap) { - HRESULT hr = S_OK; + LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; void *buf = NULL; if (path) @@ -324,20 +291,10 @@ void *d3d8_texture_new(void *_dev, #endif } - { - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; -#ifdef __cplusplus - hr = dev->CreateTexture( - width, height, miplevels, usage, - (D3DFORMAT)format, (D3DPOOL)pool, (IDirect3DTexture8**)&buf); -#else - hr = IDirect3DDevice8_CreateTexture(dev, - width, height, miplevels, usage, - (D3DFORMAT)format, (D3DPOOL)pool, (struct IDirect3DTexture8**)&buf); -#endif - } - - if (FAILED(hr)) + if (FAILED(IDirect3DDevice8_CreateTexture(dev, + width, height, miplevels, usage, + (D3DFORMAT)format, (D3DPOOL)pool, + (struct IDirect3DTexture8**)&buf))) return NULL; return buf; @@ -348,27 +305,19 @@ void d3d8_texture_free(void *_tex) LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; if (!tex) return; -#ifdef __cplusplus - tex->Release(); -#else IDirect3DTexture8_Release(tex); -#endif } bool d3d8_surface_lock_rect(void *data, void *data2) { LPDIRECT3DSURFACE8 surf = (LPDIRECT3DSURFACE8)data; - if (!surf) - return false; -#ifdef __cplusplus - if (FAILED(surf->LockRect((D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY))) - return false; -#else - if (FAILED(IDirect3DSurface8_LockRect(surf, (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY))) - return false; -#endif - - return true; + if (surf && + SUCCEEDED( + IDirect3DSurface8_LockRect( + surf, (D3DLOCKED_RECT*)data2, + NULL, D3DLOCK_READONLY))) + return true; + return false; } void d3d8_surface_unlock_rect(void *data) @@ -376,11 +325,7 @@ void d3d8_surface_unlock_rect(void *data) LPDIRECT3DSURFACE8 surf = (LPDIRECT3DSURFACE8)data; if (!surf) return; -#ifdef __cplusplus - surf->UnlockRect(); -#else IDirect3DSurface8_UnlockRect(surf); -#endif } void d3d8_surface_free(void *data) @@ -388,11 +333,7 @@ void d3d8_surface_free(void *data) LPDIRECT3DSURFACE8 surf = (LPDIRECT3DSURFACE8)data; if (!surf) return; -#ifdef __cplusplus - surf->Release(); -#else IDirect3DSurface8_Release(surf); -#endif } void *d3d8_vertex_buffer_new(void *_dev, @@ -401,17 +342,10 @@ void *d3d8_vertex_buffer_new(void *_dev, { void *buf = NULL; LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; -#ifdef __cplusplus - HRESULT hr = dev->CreateVertexBuffer( - length, usage, fvf, (D3DPOOL)pool, (IDirect3DVertexBuffer8**)&buf); -#else - HRESULT hr = IDirect3DDevice8_CreateVertexBuffer( - dev, length, usage, fvf, - (D3DPOOL)pool, - (struct IDirect3DVertexBuffer8**)&buf); -#endif - - if (FAILED(hr)) + if (FAILED(IDirect3DDevice8_CreateVertexBuffer( + dev, length, usage, fvf, + (D3DPOOL)pool, + (struct IDirect3DVertexBuffer8**)&buf))) return NULL; return buf; @@ -423,11 +357,7 @@ void d3d8_vertex_buffer_unlock(void *vertbuf_ptr) if (!vertbuf) return; -#ifdef __cplusplus - vertbuf->Unlock(); -#else IDirect3DVertexBuffer8_Unlock(vertbuf); -#endif } void *d3d8_vertex_buffer_lock(void *vertbuf_ptr) @@ -438,11 +368,7 @@ void *d3d8_vertex_buffer_lock(void *vertbuf_ptr) if (!vertbuf) return NULL; -#ifdef __cplusplus - vertbuf->Lock(0, 0, (BYTE**)&buf, 0); -#else IDirect3DVertexBuffer8_Lock(vertbuf, 0, 0, (BYTE**)&buf, 0); -#endif if (!buf) return NULL; @@ -455,11 +381,7 @@ void d3d8_vertex_buffer_free(void *vertex_data, void *vertex_declaration) if (vertex_data) { LPDIRECT3DVERTEXBUFFER8 buf = (LPDIRECT3DVERTEXBUFFER8)vertex_data; -#ifdef __cplusplus - buf->Release(); -#else IDirect3DVertexBuffer8_Release(buf); -#endif buf = NULL; } } @@ -472,24 +394,15 @@ void d3d8_set_stream_source(void *_dev, unsigned stream_no, LPDIRECT3DVERTEXBUFFER8 stream_vertbuf = (LPDIRECT3DVERTEXBUFFER8)stream_vertbuf_ptr; if (!stream_vertbuf) return; -#ifdef __cplusplus - dev->SetStreamSource(stream_no, stream_vertbuf, offset_bytes, stride); -#else IDirect3DDevice8_SetStreamSource(dev, stream_no, stream_vertbuf, stride); -#endif } static void d3d8_set_texture_stage_state(void *_dev, unsigned sampler, unsigned type, unsigned value) { LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; -#ifdef __cplusplus - if (dev->SetTextureStageState(sampler, (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) - RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); -#else if (IDirect3DDevice8_SetTextureStageState(dev, sampler, (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); -#endif } void d3d8_set_sampler_address_u(void *_dev, @@ -523,20 +436,11 @@ bool d3d8_begin_scene(void *_dev) LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; if (!dev) return false; -#ifdef __cplusplus -#ifdef _XBOX - dev->BeginScene(); -#else - if (FAILED(dev->BeginScene())) - return false; -#endif -#else #ifdef _XBOX IDirect3DDevice8_BeginScene(dev); #else if (FAILED(IDirect3DDevice8_BeginScene(dev))) return false; -#endif #endif return true; @@ -547,11 +451,7 @@ void d3d8_end_scene(void *_dev) LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; if (!dev) return; -#ifdef __cplusplus - dev->EndScene(); -#else IDirect3DDevice8_EndScene(dev); -#endif } static void d3d8_draw_primitive_internal(void *_dev, @@ -560,11 +460,7 @@ static void d3d8_draw_primitive_internal(void *_dev, LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; if (!dev) return; -#ifdef __cplusplus - dev->DrawPrimitive(type, start, count); -#else IDirect3DDevice8_DrawPrimitive(dev, type, start, count); -#endif } void d3d8_draw_primitive(void *dev, @@ -584,29 +480,18 @@ void d3d8_clear(void *_dev, LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; if (!dev) return; -#ifdef __cplusplus - dev->Clear(count, (const D3DRECT*)rects, flags, color, z, stencil); -#else IDirect3DDevice8_Clear(dev, count, (const D3DRECT*)rects, flags, color, z, stencil); -#endif } bool d3d8_device_get_render_target(void *_dev, unsigned idx, void **data) { LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (!dev) - return false; -#ifdef __cplusplus - if (SUCCEEDED(dev->GetRenderTarget( + if (dev && + SUCCEEDED(IDirect3DDevice8_GetRenderTarget(dev, (LPDIRECT3DSURFACE8*)data))) return true; -#else - if (SUCCEEDED(IDirect3DDevice8_GetRenderTarget(dev, - (LPDIRECT3DSURFACE8*)data))) - return true; -#endif return false; } @@ -618,17 +503,10 @@ bool d3d8_lock_rectangle(void *_tex, { D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; - if (!tex) - return false; -#ifdef __cplusplus - if (FAILED(tex->LockRect(level, lr, rect, flags))) - return false; -#else - if (IDirect3DTexture8_LockRect(tex, level, lr, rect, flags) != D3D_OK) - return false; -#endif - - return true; + if (tex && + IDirect3DTexture8_LockRect(tex, level, lr, rect, flags) == D3D_OK) + return true; + return false; } void d3d8_unlock_rectangle(void *_tex) @@ -636,11 +514,7 @@ void d3d8_unlock_rectangle(void *_tex) LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; if (!tex) return; -#ifdef __cplusplus - tex->UnlockRect(0); -#else IDirect3DTexture8_UnlockRect(tex, 0); -#endif } void d3d8_lock_rectangle_clear(void *tex, @@ -661,11 +535,7 @@ void d3d8_set_viewports(void *_dev, void *_vp) D3DVIEWPORT8 *vp = (D3DVIEWPORT8*)_vp; if (!dev) return; -#ifdef __cplusplus - dev->SetViewport(vp); -#else IDirect3DDevice8_SetViewport(dev, vp); -#endif } void d3d8_set_texture(void *_dev, unsigned sampler, @@ -675,27 +545,16 @@ void d3d8_set_texture(void *_dev, unsigned sampler, LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; if (!dev || !tex) return; -#ifdef __cplusplus - dev->SetTexture(sampler, tex); -#else IDirect3DDevice8_SetTexture(dev, sampler, (IDirect3DBaseTexture8*)tex); -#endif } bool d3d8_set_vertex_shader(void *_dev, unsigned index, void *data) { LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; -#ifdef __cplusplus - LPDIRECT3DVERTEXSHADER8 shader = (LPDIRECT3DVERTEXSHADER8)data; - - if (dev->SetVertexShader(shader) != D3D_OK) - return false; -#else if (IDirect3DDevice8_SetVertexShader(dev, index) != D3D_OK) return false; -#endif return true; } @@ -719,13 +578,8 @@ void d3d8_texture_blit(unsigned pixel_size, bool d3d8_get_render_state(void *data, INT32 state, DWORD *value) { LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; -#ifdef __cplusplus - if (dev && dev->GetRenderState((D3DRENDERSTATETYPE)state, value) == D3D_OK) - return true; -#else if (dev && IDirect3DDevice8_GetRenderState(dev, (D3DRENDERSTATETYPE)state, value) == D3D_OK) return true; -#endif return false; } @@ -735,11 +589,7 @@ void d3d8_set_render_state(void *data, INT32 state, DWORD value) LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; if (!dev) return; -#ifdef __cplusplus - dev->SetRenderState((D3DRENDERSTATETYPE)state, value); -#else IDirect3DDevice8_SetRenderState(dev, (D3DRENDERSTATETYPE)state, value); -#endif } void d3d8_enable_blend_func(void *data) @@ -759,11 +609,7 @@ void d3d8_device_set_render_target(void *_dev, unsigned idx, LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; if (!dev) return; -#ifdef __cplusplus - dev->SetRenderTarget(idx, surf); -#else IDirect3DDevice8_SetRenderTarget(dev, surf, NULL); -#endif } void d3d8_enable_alpha_blend_texture_func(void *data) @@ -778,18 +624,9 @@ void d3d8_frame_postprocess(void *data) { #if defined(_XBOX) global_t *global = global_get_ptr(); -#ifdef __cplusplus - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; - if (!dev) - return; - - dev->SetFlickerFilter(global->console.screen.flicker_filter_index); - dev->SetSoftDisplayFilter(global->console.softfilter_enable); -#else D3DDevice_SetFlickerFilter(global->console.screen.flicker_filter_index); D3DDevice_SetSoftDisplayFilter(global->console.softfilter_enable); #endif -#endif } void d3d8_disable_blend_func(void *data) @@ -802,34 +639,21 @@ static bool d3d8_reset_internal(void *data, ) { LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; - if (!dev) - return false; -#ifdef __cplusplus - if ((dev->Reset(d3dpp) == D3D_OK)) + if (dev && + IDirect3DDevice8_Reset(dev, d3dpp) == D3D_OK) return true; -#else - if (IDirect3DDevice8_Reset(dev, d3dpp) == D3D_OK) - return true; -#endif return false; } static HRESULT d3d8_test_cooperative_level(void *data) { -#ifdef _XBOX - return E_FAIL; -#else +#ifndef _XBOX LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; - if (!dev) - return E_FAIL; - -#ifdef __cplusplus - return dev->TestCooperativeLevel(); -#else - return IDirect3DDevice8_TestCooperativeLevel(dev); -#endif + if (dev) + return IDirect3DDevice8_TestCooperativeLevel(dev); #endif + return E_FAIL; } static bool d3d8_create_device_internal( @@ -842,10 +666,8 @@ static bool d3d8_create_device_internal( { LPDIRECT3D8 d3d = (LPDIRECT3D8)_d3d; LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; - if (!dev) - return false; -#ifdef __cplusplus - if (SUCCEEDED(d3d->CreateDevice( + if (dev && + SUCCEEDED(IDirect3D8_CreateDevice(d3d, cur_mon_id, D3DDEVTYPE_HAL, focus_window, @@ -853,16 +675,6 @@ static bool d3d8_create_device_internal( d3dpp, (IDirect3DDevice8**)dev))) return true; -#else - if (SUCCEEDED(IDirect3D8_CreateDevice(d3d, - cur_mon_id, - D3DDEVTYPE_HAL, - focus_window, - behavior_flags, - d3dpp, - (IDirect3DDevice8**)dev))) - return true; -#endif return false; } @@ -927,19 +739,11 @@ bool d3d8_device_get_backbuffer(void *_dev, unsigned backbuffer_type, void **data) { LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (!dev) - return false; -#ifdef __cplusplus - if (SUCCEEDED(dev->GetBackBuffer(idx, + if (dev && + SUCCEEDED(IDirect3DDevice8_GetBackBuffer(dev, idx, (D3DBACKBUFFER_TYPE)backbuffer_type, (LPDIRECT3DSURFACE8*)data))) return true; -#else - if (SUCCEEDED(IDirect3DDevice8_GetBackBuffer(dev, idx, - (D3DBACKBUFFER_TYPE)backbuffer_type, - (LPDIRECT3DSURFACE8*)data))) - return true; -#endif return false; } @@ -950,22 +754,10 @@ void d3d8_device_free(void *_dev, void *_pd3d) LPDIRECT3D8 pd3d = (LPDIRECT3D8)_pd3d; LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; if (dev) - { -#ifdef __cplusplus - dev->Release(); -#else IDirect3DDevice8_Release(dev); -#endif - } if (pd3d) - { -#if defined(__cplusplus) - pd3d->Release(); -#else IDirect3D8_Release(pd3d); -#endif - } } INT32 d3d8_translate_filter(unsigned type) @@ -1006,55 +798,31 @@ void d3d8x_font_draw_text(void *data, void *sprite_data, void *string_data, unsigned count, void *rect_data, unsigned format, unsigned color) { #ifdef HAVE_D3DX -#if !defined(__cplusplus) || defined(CINTERFACE) ID3DXFont *font = (ID3DXFont*)data; - if (!font) - return; - font->lpVtbl->DrawText(font, (LPD3DXSPRITE)sprite_data, - (LPCTSTR)string_data, count, (LPRECT)rect_data, - (DWORD)format, (D3DCOLOR)color); -#else - LPD3DXFONT font = (LPD3DXFONT)data; - if (!font) - return; - font->DrawText((LPD3DXSPRITE)sprite_data, - (LPCTSTR)string_data, count, (LPRECT)rect_data, - (DWORD)format, (D3DCOLOR)color); -#endif + if (font) + font->lpVtbl->DrawText(font, (LPD3DXSPRITE)sprite_data, + (LPCTSTR)string_data, count, (LPRECT)rect_data, + (DWORD)format, (D3DCOLOR)color); #endif } void d3d8x_font_release(void *data) { #ifdef HAVE_D3DX -#if !defined(__cplusplus) || defined(CINTERFACE) ID3DXFont *font = (ID3DXFont*)data; if (!font) return; font->lpVtbl->Release(font); -#else - LPD3DXFONT font = (LPD3DXFONT)data; - if (!font) - return; - font->Release(); -#endif #endif } void d3d8x_font_get_text_metrics(void *data, void *metrics) { #ifdef HAVE_D3DX -#if !defined(__cplusplus) || defined(CINTERFACE) ID3DXFont *font = (ID3DXFont*)data; if (!font) return; font->lpVtbl->GetTextMetrics(font, (TEXTMETRICA*)metrics); -#else - LPD3DXFONT font = (LPD3DXFONT)data; - if (!font) - return; - font->GetTextMetricsA((TEXTMETRICA*)metrics); -#endif #endif } From ca8829fe3c15d22955185f8b78e9cc48c36f6b2b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 14:00:32 +0200 Subject: [PATCH 386/517] (D3D9) Simplifications --- gfx/common/d3d9_common.c | 606 +----------------- gfx/common/d3d9_common.h | 591 ++++++++++++++--- gfx/common/d3d_common.h | 4 - gfx/drivers_renderchain/d3d9_cg_renderchain.c | 4 +- .../d3d9_hlsl_renderchain.c | 2 +- 5 files changed, 528 insertions(+), 679 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index 73b3134684..962fd1a02b 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -29,7 +29,7 @@ #include "../../configuration.h" #include "../../verbosity.h" -#include +#include "d3d9_common.h" #ifdef HAVE_D3DX #ifdef _XBOX @@ -41,8 +41,6 @@ #endif -#include "d3d9_common.h" - #ifdef _XBOX #include #endif @@ -242,90 +240,6 @@ void d3d9_deinitialize_symbols(void) #endif } -bool d3d9_check_device_type(void *_d3d, - unsigned idx, - INT32 disp_format, - INT32 backbuffer_format, - bool windowed_mode) -{ - LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; - if (d3d && - SUCCEEDED(IDirect3D9_CheckDeviceType(d3d, - 0, - D3DDEVTYPE_HAL, - (D3DFORMAT)disp_format, - (D3DFORMAT)backbuffer_format, - windowed_mode))) - return true; - - return false; -} - -bool d3d9_get_adapter_display_mode( - void *_d3d, - unsigned idx, - void *display_mode) -{ - LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; - if (!d3d) - return false; -#ifndef _XBOX - if (FAILED(IDirect3D9_GetAdapterDisplayMode(d3d, idx, (D3DDISPLAYMODE*)display_mode))) - return false; -#endif - - return true; -} - -bool d3d9_swap(void *data, void *_dev) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; -#ifdef _XBOX - IDirect3DDevice9_Present(dev, NULL, NULL, NULL, NULL); -#else - if (IDirect3DDevice9_Present(dev, NULL, NULL, NULL, NULL) - == D3DERR_DEVICELOST) - return false; -#endif - return true; -} - -void d3d9_set_transform(void *_dev, - INT32 state, const void *_matrix) -{ -#ifndef _XBOX - CONST D3DMATRIX *matrix = (CONST D3DMATRIX*)_matrix; - /* XBox 360 D3D9 does not support fixed-function pipeline. */ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DDevice9_SetTransform(dev, (D3DTRANSFORMSTATETYPE)state, matrix); -#endif -} - -bool d3d9_texture_get_level_desc(void *_tex, - unsigned idx, void *_ppsurface_level) -{ - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; -#if defined(_XBOX) - D3DTexture_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level); -#else - if (FAILED(IDirect3DTexture9_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level))) - return false; -#endif - return true; -} - -bool d3d9_texture_get_surface_level(void *_tex, - unsigned idx, void **_ppsurface_level) -{ - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; - if (tex && - SUCCEEDED(IDirect3DTexture9_GetSurfaceLevel( - tex, idx, (IDirect3DSurface9**)_ppsurface_level))) - return true; - - return false; -} - #ifdef HAVE_D3DX static void *d3d9_texture_new_from_file( void *dev, @@ -386,68 +300,6 @@ void *d3d9_texture_new(void *_dev, return buf; } -void d3d9_texture_free(void *_tex) -{ - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; - if (!tex) - return; - IDirect3DTexture9_Release(tex); -} - -bool d3d9_surface_lock_rect(void *data, void *data2) -{ - LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; - if (!surf) - return false; -#if defined(_XBOX) - IDirect3DSurface9_LockRect(surf, (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY); -#else - if (FAILED(IDirect3DSurface9_LockRect(surf, - (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY))) - return false; -#endif - - return true; -} - -void d3d9_surface_unlock_rect(void *data) -{ - LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; - if (!surf) - return; - IDirect3DSurface9_UnlockRect(surf); -} - -void d3d9_surface_free(void *data) -{ - LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; - if (!surf) - return; - IDirect3DSurface9_Release(surf); -} - -void d3d9_vertex_declaration_free(void *data) -{ - if (!data) - return; - - IDirect3DVertexDeclaration9_Release((LPDIRECT3DVERTEXDECLARATION9)data); -} - -bool d3d9_vertex_declaration_new(void *_dev, - const void *vertex_data, void **decl_data) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - const D3DVERTEXELEMENT9 *vertex_elements = (const D3DVERTEXELEMENT9*)vertex_data; - LPDIRECT3DVERTEXDECLARATION9 **vertex_decl = (LPDIRECT3DVERTEXDECLARATION9**)decl_data; - - if (SUCCEEDED(IDirect3DDevice9_CreateVertexDeclaration(dev, - vertex_elements, (IDirect3DVertexDeclaration9**)vertex_decl))) - return true; - - return false; -} - void *d3d9_vertex_buffer_new(void *_dev, unsigned length, unsigned usage, unsigned fvf, INT32 pool, void *handle) @@ -469,58 +321,25 @@ void *d3d9_vertex_buffer_new(void *_dev, return buf; } -void d3d9_vertex_buffer_unlock(void *vertbuf_ptr) -{ - LPDIRECT3DVERTEXBUFFER9 vertbuf = (LPDIRECT3DVERTEXBUFFER9)vertbuf_ptr; - - if (!vertbuf) - return; - IDirect3DVertexBuffer9_Unlock(vertbuf); -} - -void *d3d9_vertex_buffer_lock(void *vertbuf_ptr) -{ - void *buf = NULL; - LPDIRECT3DVERTEXBUFFER9 vertbuf = (LPDIRECT3DVERTEXBUFFER9)vertbuf_ptr; - if (!vertbuf) - return NULL; - IDirect3DVertexBuffer9_Lock(vertbuf, 0, 0, &buf, 0); - - if (!buf) - return NULL; - - return buf; -} - void d3d9_vertex_buffer_free(void *vertex_data, void *vertex_declaration) { if (vertex_data) { - LPDIRECT3DVERTEXBUFFER9 buf = (LPDIRECT3DVERTEXBUFFER9)vertex_data; + LPDIRECT3DVERTEXBUFFER9 buf = + (LPDIRECT3DVERTEXBUFFER9)vertex_data; IDirect3DVertexBuffer9_Release(buf); buf = NULL; } if (vertex_declaration) { - LPDIRECT3DVERTEXDECLARATION9 vertex_decl = (LPDIRECT3DVERTEXDECLARATION9)vertex_declaration; + LPDIRECT3DVERTEXDECLARATION9 vertex_decl = + (LPDIRECT3DVERTEXDECLARATION9)vertex_declaration; d3d9_vertex_declaration_free(vertex_decl); vertex_decl = NULL; } } -void d3d9_set_stream_source(void *_dev, unsigned stream_no, - void *stream_vertbuf_ptr, unsigned offset_bytes, - unsigned stride) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - LPDIRECT3DVERTEXBUFFER9 stream_vertbuf = (LPDIRECT3DVERTEXBUFFER9)stream_vertbuf_ptr; - if (!stream_vertbuf) - return; - IDirect3DDevice9_SetStreamSource(dev, stream_no, stream_vertbuf, - offset_bytes, - stride); -} bool d3d9_device_create_offscreen_plain_surface( void *_dev, @@ -544,112 +363,6 @@ bool d3d9_device_create_offscreen_plain_surface( return false; } -static void d3d9_set_texture_stage_state(void *_dev, - unsigned sampler, unsigned type, unsigned value) -{ -#ifndef _XBOX - /* XBox 360 has no fixed-function pipeline. */ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (IDirect3DDevice9_SetTextureStageState(dev, sampler, (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) - RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); -#endif -} - -void d3d9_set_sampler_address_u(void *_dev, - unsigned sampler, unsigned value) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_ADDRESSU, value); -} - -void d3d9_set_sampler_address_v(void *_dev, - unsigned sampler, unsigned value) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_ADDRESSV, value); -} - -void d3d9_set_sampler_minfilter(void *_dev, - unsigned sampler, unsigned value) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return; - IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_MINFILTER, value); -} - -void d3d9_set_sampler_magfilter(void *_dev, - unsigned sampler, unsigned value) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return; - IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_MAGFILTER, value); -} - -void d3d9_set_sampler_mipfilter(void *_dev, - unsigned sampler, unsigned value) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return; - IDirect3DDevice9_SetSamplerState(dev, sampler, - D3DSAMP_MIPFILTER, value); -} - -bool d3d9_begin_scene(void *_dev) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return false; -#if defined(_XBOX) - IDirect3DDevice9_BeginScene(dev); -#else - if (FAILED(IDirect3DDevice9_BeginScene(dev))) - return false; -#endif - - return true; -} - -void d3d9_end_scene(void *_dev) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return; - IDirect3DDevice9_EndScene(dev); -} - -static void d3d9_draw_primitive_internal(void *_dev, - D3DPRIMITIVETYPE type, unsigned start, unsigned count) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return; - IDirect3DDevice9_DrawPrimitive(dev, type, start, count); -} - -void d3d9_draw_primitive(void *dev, - INT32 type, unsigned start, unsigned count) -{ - if (!d3d9_begin_scene(dev)) - return; - - d3d9_draw_primitive_internal(dev, (D3DPRIMITIVETYPE)type, start, count); - d3d9_end_scene(dev); -} - -void d3d9_clear(void *_dev, - unsigned count, const void *rects, unsigned flags, - INT32 color, float z, unsigned stencil) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return; - IDirect3DDevice9_Clear(dev, count, (const D3DRECT*)rects, flags, - color, z, stencil); -} - bool d3d9_device_get_render_target_data(void *_dev, void *_src, void *_dst) { @@ -666,251 +379,6 @@ bool d3d9_device_get_render_target_data(void *_dev, return false; } -bool d3d9_device_get_render_target(void *_dev, - unsigned idx, void **data) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (dev && - SUCCEEDED(IDirect3DDevice9_GetRenderTarget(dev, - idx, (LPDIRECT3DSURFACE9*)data))) - return true; - - return false; -} - - -bool d3d9_lock_rectangle(void *_tex, - unsigned level, void *_lr, RECT *rect, - unsigned rectangle_height, unsigned flags) -{ - D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; - if (!tex) - return false; -#ifdef _XBOX - IDirect3DTexture9_LockRect(tex, level, lr, (const RECT*)rect, flags); -#else - if (IDirect3DTexture9_LockRect(tex, level, lr, (const RECT*)rect, flags) != D3D_OK) - return false; -#endif - - return true; -} - -void d3d9_unlock_rectangle(void *_tex) -{ - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; - if (!tex) - return; - IDirect3DTexture9_UnlockRect(tex, 0); -} - -void d3d9_lock_rectangle_clear(void *tex, - unsigned level, void *_lr, RECT *rect, - unsigned rectangle_height, unsigned flags) -{ - D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; -#if defined(_XBOX) - level = 0; -#endif - memset(lr->pBits, level, rectangle_height * lr->Pitch); - d3d9_unlock_rectangle(tex); -} - -void d3d9_set_viewports(void *_dev, void *_vp) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - D3DVIEWPORT9 *vp = (D3DVIEWPORT9*)_vp; - if (!dev) - return; - IDirect3DDevice9_SetViewport(dev, vp); -} - -void d3d9_set_texture(void *_dev, unsigned sampler, - void *tex_data) -{ - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)tex_data; - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev || !tex) - return; - IDirect3DDevice9_SetTexture(dev, sampler, - (IDirect3DBaseTexture9*)tex); -} - -void d3d9_free_vertex_shader(void *_dev, void *data) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DVertexShader9 *vs = (IDirect3DVertexShader9*)data; - if (!dev || !vs) - return; - IDirect3DVertexShader9_Release(vs); -} - -void d3d9_free_pixel_shader(void *_dev, void *data) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DPixelShader9 *ps = (IDirect3DPixelShader9*)data; - if (!dev || !ps) - return; - IDirect3DPixelShader9_Release(ps); -} - -bool d3d9_create_vertex_shader(void *_dev, const DWORD *a, void **b) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return false; - - if (IDirect3DDevice9_CreateVertexShader(dev, a, - (LPDIRECT3DVERTEXSHADER9*)b) == D3D_OK) - return true; - - return false; -} - -bool d3d9_create_pixel_shader(void *_dev, const DWORD *a, void **b) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - - if (dev && - IDirect3DDevice9_CreatePixelShader(dev, a, - (LPDIRECT3DPIXELSHADER9*)b) == D3D_OK) - return true; - - return false; -} - -bool d3d9_set_pixel_shader(void *_dev, void *data) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - LPDIRECT3DPIXELSHADER9 d3dps = (LPDIRECT3DPIXELSHADER9)data; - if (!dev || !d3dps) - return false; - -#ifdef _XBOX - /* Returns void on Xbox */ - IDirect3DDevice9_SetPixelShader(dev, d3dps); -#else - if (IDirect3DDevice9_SetPixelShader(dev, d3dps) != D3D_OK) - return false; -#endif - return true; -} - -bool d3d9_set_vertex_shader(void *_dev, unsigned index, - void *data) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - LPDIRECT3DVERTEXSHADER9 shader = (LPDIRECT3DVERTEXSHADER9)data; - -#ifdef _XBOX - IDirect3DDevice9_SetVertexShader(dev, shader); -#else - if (IDirect3DDevice9_SetVertexShader(dev, shader) != D3D_OK) - return false; -#endif - - return true; -} - -bool d3d9_set_vertex_shader_constantf(void *_dev, - UINT start_register,const float* constant_data, - unsigned vector4f_count) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - -#ifdef _XBOX - IDirect3DDevice9_SetVertexShaderConstantF(dev, - start_register, constant_data, vector4f_count); -#else - if (IDirect3DDevice9_SetVertexShaderConstantF(dev, - start_register, constant_data, vector4f_count) != D3D_OK) - return false; -#endif - - return true; -} - -void d3d9_texture_blit(unsigned pixel_size, - void *tex, - void *_lr, const void *frame, - unsigned width, unsigned height, unsigned pitch) -{ - unsigned y; - D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; - - for (y = 0; y < height; y++) - { - const uint8_t *in = (const uint8_t*)frame + y * pitch; - uint8_t *out = (uint8_t*)lr->pBits + y * lr->Pitch; - memcpy(out, in, width * pixel_size); - } -} - -bool d3d9_get_render_state(void *data, INT32 state, DWORD *value) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; - if (!dev) - return false; - -#ifdef _XBOX - IDirect3DDevice9_GetRenderState(dev, (D3DRENDERSTATETYPE)state, value); -#else - if (IDirect3DDevice9_GetRenderState(dev, (D3DRENDERSTATETYPE)state, value) != D3D_OK) - return false; -#endif - return true; -} - -void d3d9_set_render_state(void *data, INT32 state, DWORD value) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; - if (!dev) - return; - IDirect3DDevice9_SetRenderState(dev, (D3DRENDERSTATETYPE)state, value); -} - -void d3d9_enable_blend_func(void *data) -{ - if (!data) - return; - - d3d9_set_render_state(data, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - d3d9_set_render_state(data, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - d3d9_set_render_state(data, D3DRS_ALPHABLENDENABLE, true); -} - -void d3d9_device_set_render_target(void *_dev, unsigned idx, - void *data) -{ - LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return; - IDirect3DDevice9_SetRenderTarget(dev, idx, surf); -} - -void d3d9_enable_alpha_blend_texture_func(void *data) -{ - /* Also blend the texture with the set alpha value. */ - d3d9_set_texture_stage_state(data, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); - d3d9_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); - d3d9_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); -} - -void d3d9_disable_blend_func(void *data) -{ - d3d9_set_render_state(data, D3DRS_ALPHABLENDENABLE, false); -} - -void d3d9_set_vertex_declaration(void *data, void *vertex_data) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; - if (!dev) - return; - IDirect3DDevice9_SetVertexDeclaration(dev, (LPDIRECT3DVERTEXDECLARATION9)vertex_data); -} - static bool d3d9_reset_internal(void *data, D3DPRESENT_PARAMETERS *d3dpp ) @@ -1011,30 +479,12 @@ bool d3d9_reset(void *dev, void *d3dpp) return false; } -bool d3d9_device_get_backbuffer(void *_dev, - unsigned idx, unsigned swapchain_idx, - unsigned backbuffer_type, void **data) -{ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev) - return false; - if (SUCCEEDED(IDirect3DDevice9_GetBackBuffer(dev, - swapchain_idx, idx, - (D3DBACKBUFFER_TYPE)backbuffer_type, - (LPDIRECT3DSURFACE9*)data))) - return true; - - return false; -} - - void d3d9_device_free(void *_dev, void *_pd3d) { LPDIRECT3D9 pd3d = (LPDIRECT3D9)_pd3d; LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev) IDirect3DDevice9_Release(dev); - if (pd3d) IDirect3D9_Release(pd3d); } @@ -1119,11 +569,10 @@ void d3d9x_font_draw_text(void *data, void *sprite_data, void *string_data, { #ifdef HAVE_D3DX ID3DXFont *font = (ID3DXFont*)data; - if (!font) - return; - font->lpVtbl->DrawText(font, (LPD3DXSPRITE)sprite_data, - (LPCTSTR)string_data, count, (LPRECT)rect_data, - (DWORD)format, (D3DCOLOR)color); + if (font) + font->lpVtbl->DrawText(font, (LPD3DXSPRITE)sprite_data, + (LPCTSTR)string_data, count, (LPRECT)rect_data, + (DWORD)format, (D3DCOLOR)color); #endif } @@ -1131,9 +580,8 @@ void d3d9x_font_release(void *data) { #ifdef HAVE_D3DX ID3DXFont *font = (ID3DXFont*)data; - if (!font) - return; - font->lpVtbl->Release(font); + if (font) + font->lpVtbl->Release(font); #endif } @@ -1141,9 +589,8 @@ void d3d9x_font_get_text_metrics(void *data, void *metrics) { #ifdef HAVE_D3DX ID3DXFont *font = (ID3DXFont*)data; - if (!font) - return; - font->lpVtbl->GetTextMetrics(font, (TEXTMETRICA*)metrics); + if (font) + font->lpVtbl->GetTextMetrics(font, (TEXTMETRICA*)metrics); #endif } @@ -1175,33 +622,6 @@ bool d3d9x_compile_shader_from_file( return false; } -INT32 d3d9_get_rgb565_format(void) -{ -#ifdef _XBOX - return D3DFMT_LIN_R5G6B5; -#else - return D3DFMT_R5G6B5; -#endif -} - -INT32 d3d9_get_argb8888_format(void) -{ -#ifdef _XBOX - return D3DFMT_LIN_A8R8G8B8; -#else - return D3DFMT_A8R8G8B8; -#endif -} - -INT32 d3d9_get_xrgb8888_format(void) -{ -#ifdef _XBOX - return D3DFMT_LIN_X8R8G8B8; -#else - return D3DFMT_X8R8G8B8; -#endif -} - const void *d3d9x_get_buffer_ptr(void *data) { #if defined(HAVE_D3DX) diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index 008ae8cde3..49799ac78a 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -18,27 +18,81 @@ #include #include +#include + +#include #include "../video_driver.h" +#include "../../verbosity.h" RETRO_BEGIN_DECLS -bool d3d9_swap(void *data, void *dev); +static INLINE bool d3d9_swap(void *data, void *_dev) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; +#ifdef _XBOX + IDirect3DDevice9_Present(dev, NULL, NULL, NULL, NULL); +#else + if (IDirect3DDevice9_Present(dev, NULL, NULL, NULL, NULL) + == D3DERR_DEVICELOST) + return false; +#endif + return true; +} void *d3d9_vertex_buffer_new(void *dev, unsigned length, unsigned usage, unsigned fvf, INT32 pool, void *handle); -void *d3d9_vertex_buffer_lock(void *data); -void d3d9_vertex_buffer_unlock(void *data); +static INLINE void *d3d9_vertex_buffer_lock(void *vertbuf_ptr) +{ + void *buf = NULL; + LPDIRECT3DVERTEXBUFFER9 vertbuf = (LPDIRECT3DVERTEXBUFFER9)vertbuf_ptr; + if (!vertbuf) + return NULL; + IDirect3DVertexBuffer9_Lock(vertbuf, 0, 0, &buf, 0); + + if (!buf) + return NULL; + + return buf; +} + +static INLINE void d3d9_vertex_buffer_unlock(void *vertbuf_ptr) +{ + LPDIRECT3DVERTEXBUFFER9 vertbuf = (LPDIRECT3DVERTEXBUFFER9)vertbuf_ptr; + + if (!vertbuf) + return; + IDirect3DVertexBuffer9_Unlock(vertbuf); +} void d3d9_vertex_buffer_free(void *vertex_data, void *vertex_declaration); -bool d3d9_texture_get_level_desc(void *tex, - unsigned idx, void *_ppsurface_level); +static INLINE bool d3d9_texture_get_level_desc(void *_tex, + unsigned idx, void *_ppsurface_level) +{ + LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; +#if defined(_XBOX) + D3DTexture_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level); +#else + if (FAILED(IDirect3DTexture9_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level))) + return false; +#endif + return true; +} -bool d3d9_texture_get_surface_level(void *tex, - unsigned idx, void **_ppsurface_level); +static INLINE bool d3d9_texture_get_surface_level(void *_tex, + unsigned idx, void **_ppsurface_level) +{ + LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; + if (tex && + SUCCEEDED(IDirect3DTexture9_GetSurfaceLevel( + tex, idx, (IDirect3DSurface9**)_ppsurface_level))) + return true; + + return false; +} void *d3d9_texture_new(void *dev, const char *path, unsigned width, unsigned height, @@ -47,110 +101,410 @@ void *d3d9_texture_new(void *dev, INT32 color_key, void *src_info, PALETTEENTRY *palette, bool want_mipmap); -void d3d9_set_stream_source(void *dev, unsigned stream_no, - void *stream_vertbuf, unsigned offset_bytes, - unsigned stride); +static INLINE void d3d9_set_stream_source( + void *_dev, unsigned stream_no, + void *stream_vertbuf_ptr, unsigned offset_bytes, + unsigned stride) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + LPDIRECT3DVERTEXBUFFER9 + stream_vertbuf = (LPDIRECT3DVERTEXBUFFER9)stream_vertbuf_ptr; + if (stream_vertbuf) + IDirect3DDevice9_SetStreamSource(dev, stream_no, stream_vertbuf, + offset_bytes, + stride); +} -void d3d9_texture_free(void *tex); +static INLINE void d3d9_texture_free(void *_tex) +{ + LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; + if (!tex) + return; + IDirect3DTexture9_Release(tex); +} -void d3d9_set_transform(void *dev, - INT32 state, const void *_matrix); +static INLINE void d3d9_set_transform(void *_dev, + INT32 state, const void *_matrix) +{ +#ifndef _XBOX + CONST D3DMATRIX *matrix = (CONST D3DMATRIX*)_matrix; + /* XBox 360 D3D9 does not support fixed-function pipeline. */ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + IDirect3DDevice9_SetTransform(dev, (D3DTRANSFORMSTATETYPE)state, matrix); +#endif +} -void d3d9_set_sampler_address_u(void *dev, - unsigned sampler, unsigned value); +static INLINE void d3d9_set_sampler_address_u(void *_dev, + unsigned sampler, unsigned value) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_ADDRESSU, value); +} -void d3d9_set_sampler_address_v(void *dev, - unsigned sampler, unsigned value); +static INLINE void d3d9_set_sampler_address_v(void *_dev, + unsigned sampler, unsigned value) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_ADDRESSV, value); +} -void d3d9_set_sampler_minfilter(void *dev, - unsigned sampler, unsigned value); +static INLINE void d3d9_set_sampler_minfilter(void *_dev, + unsigned sampler, unsigned value) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev) + IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_MINFILTER, value); +} -void d3d9_set_sampler_magfilter(void *dev, - unsigned sampler, unsigned value); +static INLINE void d3d9_set_sampler_magfilter(void *_dev, + unsigned sampler, unsigned value) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev) + IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_MAGFILTER, value); +} -void d3d9_set_sampler_mipfilter(void *dev, - unsigned sampler, unsigned value); +static INLINE void d3d9_set_sampler_mipfilter(void *_dev, + unsigned sampler, unsigned value) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev) + IDirect3DDevice9_SetSamplerState(dev, sampler, + D3DSAMP_MIPFILTER, value); +} -bool d3d9_begin_scene(void *dev); +static INLINE bool d3d9_begin_scene(void *_dev) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (!dev) + return false; +#if defined(_XBOX) + IDirect3DDevice9_BeginScene(dev); +#else + if (FAILED(IDirect3DDevice9_BeginScene(dev))) + return false; +#endif -void d3d9_end_scene(void *dev); + return true; +} -void d3d9_draw_primitive(void *dev, - INT32 type, unsigned start, unsigned count); +static INLINE void d3d9_end_scene(void *_dev) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev) + IDirect3DDevice9_EndScene(dev); +} -void d3d9_clear(void *dev, +static INLINE void d3d9_draw_primitive(void *_dev, + INT32 _type, unsigned start, unsigned count) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + D3DPRIMITIVETYPE type = (D3DPRIMITIVETYPE)_type; + if (!dev || !d3d9_begin_scene(dev)) + return; + IDirect3DDevice9_DrawPrimitive(dev, type, start, count); + d3d9_end_scene(dev); +} + +static INLINE void d3d9_clear(void *_dev, unsigned count, const void *rects, unsigned flags, - INT32 color, float z, unsigned stencil); + INT32 color, float z, unsigned stencil) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev) + IDirect3DDevice9_Clear(dev, count, (const D3DRECT*)rects, flags, + color, z, stencil); +} -bool d3d9_lock_rectangle(void *tex, - unsigned level, void *lock_rect, RECT *rect, - unsigned rectangle_height, unsigned flags); +static INLINE bool d3d9_lock_rectangle(void *_tex, + unsigned level, void *_lr, RECT *rect, + unsigned rectangle_height, unsigned flags) +{ + D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; + LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; + if (!tex) + return false; +#ifdef _XBOX + IDirect3DTexture9_LockRect(tex, level, lr, (const RECT*)rect, flags); +#else + if (IDirect3DTexture9_LockRect(tex, level, lr, (const RECT*)rect, flags) != D3D_OK) + return false; +#endif -void d3d9_lock_rectangle_clear(void *tex, - unsigned level, void *lock_rect, RECT *rect, - unsigned rectangle_height, unsigned flags); + return true; +} -void d3d9_unlock_rectangle(void *tex); +static INLINE void d3d9_unlock_rectangle(void *_tex) +{ + LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; + if (tex) + IDirect3DTexture9_UnlockRect(tex, 0); +} -void d3d9_set_texture(void *dev, unsigned sampler, - void *tex_data); +static INLINE void d3d9_lock_rectangle_clear(void *tex, + unsigned level, void *_lr, RECT *rect, + unsigned rectangle_height, unsigned flags) +{ + D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; +#if defined(_XBOX) + level = 0; +#endif + memset(lr->pBits, level, rectangle_height * lr->Pitch); + d3d9_unlock_rectangle(tex); +} -bool d3d9_create_vertex_shader(void *dev, - const DWORD *a, void **b); +static INLINE void d3d9_set_texture(void *_dev, unsigned sampler, + void *tex_data) +{ + LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)tex_data; + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (!dev || !tex) + return; + IDirect3DDevice9_SetTexture(dev, sampler, + (IDirect3DBaseTexture9*)tex); +} -bool d3d9_create_pixel_shader(void *dev, - const DWORD *a, void **b); +static INLINE bool d3d9_create_vertex_shader( + void *_dev, const DWORD *a, void **b) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev && IDirect3DDevice9_CreateVertexShader(dev, a, + (LPDIRECT3DVERTEXSHADER9*)b) == D3D_OK) + return true; + return false; +} -void d3d9_free_pixel_shader(void *dev, void *data); +static INLINE bool d3d9_create_pixel_shader( + void *_dev, const DWORD *a, void **b) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev && + IDirect3DDevice9_CreatePixelShader(dev, a, + (LPDIRECT3DPIXELSHADER9*)b) == D3D_OK) + return true; + return false; +} -void d3d9_free_vertex_shader(void *dev, void *data); +static INLINE void d3d9_free_vertex_shader(void *_dev, void *data) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + IDirect3DVertexShader9 *vs = (IDirect3DVertexShader9*)data; + if (!dev || !vs) + return; + IDirect3DVertexShader9_Release(vs); +} -bool d3d9_set_pixel_shader(void *dev, void *data); +static INLINE void d3d9_free_pixel_shader(void *_dev, void *data) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + IDirect3DPixelShader9 *ps = (IDirect3DPixelShader9*)data; + if (!dev || !ps) + return; + IDirect3DPixelShader9_Release(ps); +} -bool d3d9_set_vertex_shader(void *dev, unsigned index, - void *data); +static INLINE bool d3d9_set_pixel_shader( + void *_dev, void *data) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + LPDIRECT3DPIXELSHADER9 d3dps = (LPDIRECT3DPIXELSHADER9)data; + if (!dev || !d3dps) + return false; -bool d3d9_set_vertex_shader_constantf(void *dev, - UINT start_register,const float* constant_data, unsigned vector4f_count); +#ifdef _XBOX + /* Returns void on Xbox */ + IDirect3DDevice9_SetPixelShader(dev, d3dps); +#else + if (IDirect3DDevice9_SetPixelShader(dev, d3dps) != D3D_OK) + return false; +#endif + return true; +} -void d3d9_texture_blit(unsigned pixel_size, +static INLINE bool d3d9_set_vertex_shader( + void *_dev, unsigned index, + void *data) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + LPDIRECT3DVERTEXSHADER9 shader = (LPDIRECT3DVERTEXSHADER9)data; + +#ifdef _XBOX + IDirect3DDevice9_SetVertexShader(dev, shader); +#else + if (IDirect3DDevice9_SetVertexShader(dev, shader) != D3D_OK) + return false; +#endif + + return true; +} + +static INLINE bool d3d9_set_vertex_shader_constantf(void *_dev, + UINT start_register,const float* constant_data, + unsigned vector4f_count) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + +#ifdef _XBOX + IDirect3DDevice9_SetVertexShaderConstantF(dev, + start_register, constant_data, vector4f_count); +#else + if (IDirect3DDevice9_SetVertexShaderConstantF(dev, + start_register, constant_data, vector4f_count) != D3D_OK) + return false; +#endif + + return true; +} + +static INLINE void d3d9_texture_blit(unsigned pixel_size, void *tex, - void *lr, const void *frame, - unsigned width, unsigned height, unsigned pitch); + void *_lr, const void *frame, + unsigned width, unsigned height, unsigned pitch) +{ + unsigned y; + D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; -bool d3d9_vertex_declaration_new(void *dev, - const void *vertex_data, void **decl_data); + for (y = 0; y < height; y++) + { + const uint8_t *in = (const uint8_t*)frame + y * pitch; + uint8_t *out = (uint8_t*)lr->pBits + y * lr->Pitch; + memcpy(out, in, width * pixel_size); + } +} -void d3d9_vertex_declaration_free(void *data); +static INLINE bool d3d9_vertex_declaration_new(void *_dev, + const void *vertex_data, void **decl_data) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + const D3DVERTEXELEMENT9 *vertex_elements = (const D3DVERTEXELEMENT9*)vertex_data; + LPDIRECT3DVERTEXDECLARATION9 **vertex_decl = (LPDIRECT3DVERTEXDECLARATION9**)decl_data; -void d3d9_set_viewports(void *dev, void *vp); + if (SUCCEEDED(IDirect3DDevice9_CreateVertexDeclaration(dev, + vertex_elements, (IDirect3DVertexDeclaration9**)vertex_decl))) + return true; -void d3d9_enable_blend_func(void *data); + return false; +} -void d3d9_disable_blend_func(void *data); +static INLINE void d3d9_vertex_declaration_free(void *data) +{ + if (!data) + return; -void d3d9_set_vertex_declaration(void *data, void *vertex_data); + IDirect3DVertexDeclaration9_Release((LPDIRECT3DVERTEXDECLARATION9)data); +} -void d3d9_enable_alpha_blend_texture_func(void *data); +static INLINE void d3d9_set_viewports(void *_dev, void *_vp) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + D3DVIEWPORT9 *vp = (D3DVIEWPORT9*)_vp; + if (dev) + IDirect3DDevice9_SetViewport(dev, vp); +} + +static INLINE void d3d9_set_render_state( + LPDIRECT3DDEVICE9 dev, D3DRENDERSTATETYPE state, DWORD value) +{ + IDirect3DDevice9_SetRenderState(dev, state, value); +} + +static INLINE void d3d9_enable_blend_func(void *data) +{ + if (!data) + return; + + d3d9_set_render_state(data, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + d3d9_set_render_state(data, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + d3d9_set_render_state(data, D3DRS_ALPHABLENDENABLE, true); +} + +static INLINE void d3d9_disable_blend_func(void *data) +{ + d3d9_set_render_state(data, D3DRS_ALPHABLENDENABLE, false); +} + +static INLINE void +d3d9_set_vertex_declaration(void *data, void *vertex_data) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; + if (dev) + IDirect3DDevice9_SetVertexDeclaration(dev, + (LPDIRECT3DVERTEXDECLARATION9)vertex_data); +} + +static INLINE void d3d9_set_texture_stage_state(void *_dev, + unsigned sampler, unsigned type, unsigned value) +{ +#ifndef _XBOX + /* XBox 360 has no fixed-function pipeline. */ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (IDirect3DDevice9_SetTextureStageState(dev, sampler, + (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) + RARCH_ERR("SetTextureStageState call failed, sampler" + ": %d, value: %d, type: %d\n", sampler, value, type); +#endif +} + +static INLINE void d3d9_enable_alpha_blend_texture_func(void *data) +{ + /* Also blend the texture with the set alpha value. */ + d3d9_set_texture_stage_state(data, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + d3d9_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + d3d9_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); +} void d3d9_frame_postprocess(void *data); -void d3d9_surface_free(void *data); +static INLINE void d3d9_surface_free(void *data) +{ + LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; + if (!surf) + return; + IDirect3DSurface9_Release(surf); +} bool d3d9_device_get_render_target_data(void *dev, void *_src, void *_dst); -bool d3d9_device_get_render_target(void *dev, - unsigned idx, void **data); +static INLINE bool d3d9_device_get_render_target(void *_dev, + unsigned idx, void **data) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev && + SUCCEEDED(IDirect3DDevice9_GetRenderTarget(dev, + idx, (LPDIRECT3DSURFACE9*)data))) + return true; -void d3d9_device_set_render_target(void *dev, unsigned idx, - void *data); + return false; +} -bool d3d9_get_render_state(void *data, - INT32 state, DWORD *value); +static INLINE void d3d9_device_set_render_target( + void *_dev, unsigned idx, + void *data) +{ + LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev) + IDirect3DDevice9_SetRenderTarget(dev, idx, surf); +} -void d3d9_set_render_state(void *data, - INT32 state, DWORD value); +static INLINE bool d3d9_get_render_state( + void *data, INT32 state, DWORD *value) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; + if (!dev) + return false; + +#ifdef _XBOX + IDirect3DDevice9_GetRenderState(dev, + (D3DRENDERSTATETYPE)state, value); +#else + if (IDirect3DDevice9_GetRenderState(dev, + (D3DRENDERSTATETYPE)state, value) != D3D_OK) + return false; +#endif + return true; +} void d3d9_device_set_render_target(void *dev, unsigned idx, void *data); @@ -164,13 +518,46 @@ bool d3d9_device_create_offscreen_plain_surface( void **surf_data, void *data); -bool d3d9_surface_lock_rect(void *data, void *data2); +static INLINE bool d3d9_surface_lock_rect(void *data, void *data2) +{ + LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; + if (!surf) + return false; +#if defined(_XBOX) + IDirect3DSurface9_LockRect(surf, (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY); +#else + if (FAILED(IDirect3DSurface9_LockRect(surf, + (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY))) + return false; +#endif + return true; +} + +static INLINE void d3d9_surface_unlock_rect(void *data) +{ + LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; + if (!surf) + return; + IDirect3DSurface9_UnlockRect(surf); +} void d3d9_surface_unlock_rect(void *data); -bool d3d9_get_adapter_display_mode(void *d3d, +static INLINE bool d3d9_get_adapter_display_mode( + void *_d3d, unsigned idx, - void *display_mode); + void *display_mode) +{ + LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; + if (!d3d) + return false; +#ifndef _XBOX + if (FAILED(IDirect3D9_GetAdapterDisplayMode(d3d, idx, (D3DDISPLAYMODE*)display_mode))) + return false; +#endif + + return true; +} bool d3d9_create_device(void *dev, void *d3dpp, @@ -180,9 +567,19 @@ bool d3d9_create_device(void *dev, bool d3d9_reset(void *dev, void *d3dpp); -bool d3d9_device_get_backbuffer(void *dev, +static INLINE bool d3d9_device_get_backbuffer(void *_dev, unsigned idx, unsigned swapchain_idx, - unsigned backbuffer_type, void **data); + unsigned backbuffer_type, void **data) +{ + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev && + SUCCEEDED(IDirect3DDevice9_GetBackBuffer(dev, + swapchain_idx, idx, + (D3DBACKBUFFER_TYPE)backbuffer_type, + (LPDIRECT3DSURFACE9*)data))) + return true; + return false; +} void d3d9_device_free(void *dev, void *pd3d); @@ -192,11 +589,24 @@ bool d3d9_initialize_symbols(enum gfx_ctx_api api); void d3d9_deinitialize_symbols(void); -bool d3d9_check_device_type(void *d3d, +static INLINE bool d3d9_check_device_type(void *_d3d, unsigned idx, INT32 disp_format, INT32 backbuffer_format, - bool windowed_mode); + bool windowed_mode) +{ + LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; + if (d3d && + SUCCEEDED(IDirect3D9_CheckDeviceType(d3d, + 0, + D3DDEVTYPE_HAL, + (D3DFORMAT)disp_format, + (D3DFORMAT)backbuffer_format, + windowed_mode))) + return true; + + return false; +} bool d3d9x_create_font_indirect(void *dev, void *desc, void **font_data); @@ -240,9 +650,32 @@ const void *d3d9x_get_buffer_ptr(void *data); const bool d3d9x_constant_table_set_float(void *p, void *a, const void *b, float val); -INT32 d3d9_get_rgb565_format(void); -INT32 d3d9_get_argb8888_format(void); -INT32 d3d9_get_xrgb8888_format(void); +static INLINE INT32 d3d9_get_rgb565_format(void) +{ +#ifdef _XBOX + return D3DFMT_LIN_R5G6B5; +#else + return D3DFMT_R5G6B5; +#endif +} + +static INLINE INT32 d3d9_get_argb8888_format(void) +{ +#ifdef _XBOX + return D3DFMT_LIN_A8R8G8B8; +#else + return D3DFMT_A8R8G8B8; +#endif +} + +static INLINE INT32 d3d9_get_xrgb8888_format(void) +{ +#ifdef _XBOX + return D3DFMT_LIN_X8R8G8B8; +#else + return D3DFMT_X8R8G8B8; +#endif +} RETRO_END_DECLS diff --git a/gfx/common/d3d_common.h b/gfx/common/d3d_common.h index 2899e3e8fa..e34809f1f4 100644 --- a/gfx/common/d3d_common.h +++ b/gfx/common/d3d_common.h @@ -33,10 +33,6 @@ typedef struct d3d_texture #define BYTE_CLAMP(i) (int) ((((i) > 255) ? 255 : (((i) < 0) ? 0 : (i)))) #endif -#ifndef D3DCOLOR_ARGB -#define D3DCOLOR_ARGB(_a, _r, _g, _b) ( (DWORD)( ( ( (_a)&0xff)<<24)|( ( (_r)&0xff)<<16)|( ( (_g)&0xff)<<8)|( (_b)&0xff) ) ) -#endif - #define D3DTADDRESS_COMM_CLAMP 3 #define D3DTEXF_COMM_LINEAR 2 #define D3DPT_COMM_TRIANGLESTRIP 5 diff --git a/gfx/drivers_renderchain/d3d9_cg_renderchain.c b/gfx/drivers_renderchain/d3d9_cg_renderchain.c index ed8308e1a6..d3406b3307 100644 --- a/gfx/drivers_renderchain/d3d9_cg_renderchain.c +++ b/gfx/drivers_renderchain/d3d9_cg_renderchain.c @@ -1331,8 +1331,8 @@ static void cg_d3d9_renderchain_blit_to_texture( unsigned width, unsigned height, unsigned pitch) { - D3DLOCKED_RECT d3dlr; - struct Pass *first = (struct Pass*)&chain->passes->data[0]; + D3DLOCKED_RECT d3dlr = {0, NULL}; + struct Pass *first = (struct Pass*)&chain->passes->data[0]; if ( (first->last_width != width || first->last_height != height) diff --git a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c index 0f61b2b0b3..8e5d2e7aac 100644 --- a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c +++ b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c @@ -201,7 +201,7 @@ static void hlsl_d3d9_renderchain_blit_to_texture( void *data, const void *frame, unsigned width, unsigned height, unsigned pitch) { - D3DLOCKED_RECT d3dlr; + D3DLOCKED_RECT d3dlr = { 0, NULL }; hlsl_d3d9_renderchain_t *chain = (hlsl_d3d9_renderchain_t*)data; if (chain->last_width != width || chain->last_height != height) From 80fdd6d2a19a770c7c35cb6abf3d647908ad072e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 14:20:59 +0200 Subject: [PATCH 387/517] Less pointer/variable casting going on --- gfx/common/d3d9_common.h | 306 ++++++++++++++++++--------------------- 1 file changed, 144 insertions(+), 162 deletions(-) diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index 49799ac78a..9ccc5e6517 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -27,9 +27,8 @@ RETRO_BEGIN_DECLS -static INLINE bool d3d9_swap(void *data, void *_dev) +static INLINE bool d3d9_swap(void *data, LPDIRECT3DDEVICE9 dev) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; #ifdef _XBOX IDirect3DDevice9_Present(dev, NULL, NULL, NULL, NULL); #else @@ -44,10 +43,9 @@ void *d3d9_vertex_buffer_new(void *dev, unsigned length, unsigned usage, unsigned fvf, INT32 pool, void *handle); -static INLINE void *d3d9_vertex_buffer_lock(void *vertbuf_ptr) +static INLINE void *d3d9_vertex_buffer_lock(LPDIRECT3DVERTEXBUFFER9 vertbuf) { - void *buf = NULL; - LPDIRECT3DVERTEXBUFFER9 vertbuf = (LPDIRECT3DVERTEXBUFFER9)vertbuf_ptr; + void *buf = NULL; if (!vertbuf) return NULL; IDirect3DVertexBuffer9_Lock(vertbuf, 0, 0, &buf, 0); @@ -58,39 +56,36 @@ static INLINE void *d3d9_vertex_buffer_lock(void *vertbuf_ptr) return buf; } -static INLINE void d3d9_vertex_buffer_unlock(void *vertbuf_ptr) +static INLINE void d3d9_vertex_buffer_unlock(LPDIRECT3DVERTEXBUFFER9 vertbuf) { - LPDIRECT3DVERTEXBUFFER9 vertbuf = (LPDIRECT3DVERTEXBUFFER9)vertbuf_ptr; - - if (!vertbuf) - return; - IDirect3DVertexBuffer9_Unlock(vertbuf); + if (vertbuf) + IDirect3DVertexBuffer9_Unlock(vertbuf); } void d3d9_vertex_buffer_free(void *vertex_data, void *vertex_declaration); -static INLINE bool d3d9_texture_get_level_desc(void *_tex, - unsigned idx, void *_ppsurface_level) +static INLINE bool d3d9_texture_get_level_desc( + LPDIRECT3DTEXTURE9 tex, + unsigned idx, + D3DSURFACE_DESC *_ppsurface_level) { - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; #if defined(_XBOX) - D3DTexture_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level); + D3DTexture_GetLevelDesc(tex, idx, _ppsurface_level); #else - if (FAILED(IDirect3DTexture9_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level))) + if (FAILED(IDirect3DTexture9_GetLevelDesc(tex, idx, _ppsurface_level))) return false; #endif return true; } -static INLINE bool d3d9_texture_get_surface_level(void *_tex, +static INLINE bool d3d9_texture_get_surface_level( + LPDIRECT3DTEXTURE9 tex, unsigned idx, void **_ppsurface_level) { - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; if (tex && SUCCEEDED(IDirect3DTexture9_GetSurfaceLevel( tex, idx, (IDirect3DSurface9**)_ppsurface_level))) return true; - return false; } @@ -102,80 +97,81 @@ void *d3d9_texture_new(void *dev, PALETTEENTRY *palette, bool want_mipmap); static INLINE void d3d9_set_stream_source( - void *_dev, unsigned stream_no, - void *stream_vertbuf_ptr, unsigned offset_bytes, + LPDIRECT3DDEVICE9 dev, + unsigned stream_no, + LPDIRECT3DVERTEXBUFFER9 stream_vertbuf, + unsigned offset_bytes, unsigned stride) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - LPDIRECT3DVERTEXBUFFER9 - stream_vertbuf = (LPDIRECT3DVERTEXBUFFER9)stream_vertbuf_ptr; if (stream_vertbuf) IDirect3DDevice9_SetStreamSource(dev, stream_no, stream_vertbuf, offset_bytes, stride); } -static INLINE void d3d9_texture_free(void *_tex) +static INLINE void d3d9_texture_free(LPDIRECT3DTEXTURE9 tex) { - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; - if (!tex) - return; - IDirect3DTexture9_Release(tex); + if (tex) + IDirect3DTexture9_Release(tex); } -static INLINE void d3d9_set_transform(void *_dev, - INT32 state, const void *_matrix) +static INLINE void d3d9_set_transform( + LPDIRECT3DDEVICE9 dev, + D3DTRANSFORMSTATETYPE state, + const void *_matrix) { #ifndef _XBOX CONST D3DMATRIX *matrix = (CONST D3DMATRIX*)_matrix; /* XBox 360 D3D9 does not support fixed-function pipeline. */ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DDevice9_SetTransform(dev, (D3DTRANSFORMSTATETYPE)state, matrix); + IDirect3DDevice9_SetTransform(dev, state, matrix); #endif } -static INLINE void d3d9_set_sampler_address_u(void *_dev, +static INLINE void d3d9_set_sampler_address_u( + LPDIRECT3DDEVICE9 dev, unsigned sampler, unsigned value) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_ADDRESSU, value); + IDirect3DDevice9_SetSamplerState(dev, + sampler, D3DSAMP_ADDRESSU, value); } -static INLINE void d3d9_set_sampler_address_v(void *_dev, +static INLINE void d3d9_set_sampler_address_v( + LPDIRECT3DDEVICE9 dev, unsigned sampler, unsigned value) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_ADDRESSV, value); + IDirect3DDevice9_SetSamplerState(dev, + sampler, D3DSAMP_ADDRESSV, value); } -static INLINE void d3d9_set_sampler_minfilter(void *_dev, +static INLINE void d3d9_set_sampler_minfilter( + LPDIRECT3DDEVICE9 dev, unsigned sampler, unsigned value) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev) - IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_MINFILTER, value); + IDirect3DDevice9_SetSamplerState(dev, + sampler, D3DSAMP_MINFILTER, value); } -static INLINE void d3d9_set_sampler_magfilter(void *_dev, +static INLINE void d3d9_set_sampler_magfilter( + LPDIRECT3DDEVICE9 dev, unsigned sampler, unsigned value) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev) - IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_MAGFILTER, value); + IDirect3DDevice9_SetSamplerState(dev, + sampler, D3DSAMP_MAGFILTER, value); } -static INLINE void d3d9_set_sampler_mipfilter(void *_dev, +static INLINE void d3d9_set_sampler_mipfilter( + LPDIRECT3DDEVICE9 dev, unsigned sampler, unsigned value) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev) IDirect3DDevice9_SetSamplerState(dev, sampler, D3DSAMP_MIPFILTER, value); } -static INLINE bool d3d9_begin_scene(void *_dev) +static INLINE bool d3d9_begin_scene(LPDIRECT3DDEVICE9 dev) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (!dev) return false; #if defined(_XBOX) @@ -188,55 +184,54 @@ static INLINE bool d3d9_begin_scene(void *_dev) return true; } -static INLINE void d3d9_end_scene(void *_dev) +static INLINE void d3d9_end_scene(LPDIRECT3DDEVICE9 dev) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev) IDirect3DDevice9_EndScene(dev); } -static INLINE void d3d9_draw_primitive(void *_dev, - INT32 _type, unsigned start, unsigned count) +static INLINE void d3d9_draw_primitive( + LPDIRECT3DDEVICE9 dev, + D3DPRIMITIVETYPE type, + unsigned start, unsigned count) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - D3DPRIMITIVETYPE type = (D3DPRIMITIVETYPE)_type; if (!dev || !d3d9_begin_scene(dev)) return; IDirect3DDevice9_DrawPrimitive(dev, type, start, count); d3d9_end_scene(dev); } -static INLINE void d3d9_clear(void *_dev, - unsigned count, const void *rects, unsigned flags, +static INLINE void d3d9_clear( + LPDIRECT3DDEVICE9 dev, + unsigned count, const D3DRECT *rects, unsigned flags, INT32 color, float z, unsigned stencil) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev) - IDirect3DDevice9_Clear(dev, count, (const D3DRECT*)rects, flags, + IDirect3DDevice9_Clear(dev, count, rects, flags, color, z, stencil); } -static INLINE bool d3d9_lock_rectangle(void *_tex, - unsigned level, void *_lr, RECT *rect, +static INLINE bool d3d9_lock_rectangle( + LPDIRECT3DTEXTURE9 tex, + unsigned level, + D3DLOCKED_RECT *lr, + const RECT *rect, unsigned rectangle_height, unsigned flags) { - D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; if (!tex) return false; #ifdef _XBOX - IDirect3DTexture9_LockRect(tex, level, lr, (const RECT*)rect, flags); + IDirect3DTexture9_LockRect(tex, level, lr, rect, flags); #else - if (IDirect3DTexture9_LockRect(tex, level, lr, (const RECT*)rect, flags) != D3D_OK) + if (IDirect3DTexture9_LockRect(tex, level, lr, rect, flags) != D3D_OK) return false; #endif return true; } -static INLINE void d3d9_unlock_rectangle(void *_tex) +static INLINE void d3d9_unlock_rectangle(LPDIRECT3DTEXTURE9 tex) { - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)_tex; if (tex) IDirect3DTexture9_UnlockRect(tex, 0); } @@ -253,21 +248,19 @@ static INLINE void d3d9_lock_rectangle_clear(void *tex, d3d9_unlock_rectangle(tex); } -static INLINE void d3d9_set_texture(void *_dev, unsigned sampler, - void *tex_data) +static INLINE void d3d9_set_texture( + LPDIRECT3DDEVICE9 dev, + unsigned sampler, + LPDIRECT3DTEXTURE9 tex) { - LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)tex_data; - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (!dev || !tex) - return; - IDirect3DDevice9_SetTexture(dev, sampler, - (IDirect3DBaseTexture9*)tex); + if (dev && tex) + IDirect3DDevice9_SetTexture(dev, sampler, + (IDirect3DBaseTexture9*)tex); } static INLINE bool d3d9_create_vertex_shader( - void *_dev, const DWORD *a, void **b) + LPDIRECT3DDEVICE9 dev, const DWORD *a, void **b) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev && IDirect3DDevice9_CreateVertexShader(dev, a, (LPDIRECT3DVERTEXSHADER9*)b) == D3D_OK) return true; @@ -275,9 +268,8 @@ static INLINE bool d3d9_create_vertex_shader( } static INLINE bool d3d9_create_pixel_shader( - void *_dev, const DWORD *a, void **b) + LPDIRECT3DDEVICE9 dev, const DWORD *a, void **b) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev && IDirect3DDevice9_CreatePixelShader(dev, a, (LPDIRECT3DPIXELSHADER9*)b) == D3D_OK) @@ -285,29 +277,24 @@ static INLINE bool d3d9_create_pixel_shader( return false; } -static INLINE void d3d9_free_vertex_shader(void *_dev, void *data) +static INLINE void d3d9_free_vertex_shader( + LPDIRECT3DDEVICE9 dev, IDirect3DVertexShader9 *vs) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DVertexShader9 *vs = (IDirect3DVertexShader9*)data; - if (!dev || !vs) - return; - IDirect3DVertexShader9_Release(vs); + if (dev && vs) + IDirect3DVertexShader9_Release(vs); } -static INLINE void d3d9_free_pixel_shader(void *_dev, void *data) +static INLINE void d3d9_free_pixel_shader(LPDIRECT3DDEVICE9 dev, + IDirect3DPixelShader9 *ps) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - IDirect3DPixelShader9 *ps = (IDirect3DPixelShader9*)data; - if (!dev || !ps) - return; - IDirect3DPixelShader9_Release(ps); + if (dev && ps) + IDirect3DPixelShader9_Release(ps); } static INLINE bool d3d9_set_pixel_shader( - void *_dev, void *data) + LPDIRECT3DDEVICE9 dev, + LPDIRECT3DPIXELSHADER9 d3dps) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - LPDIRECT3DPIXELSHADER9 d3dps = (LPDIRECT3DPIXELSHADER9)data; if (!dev || !d3dps) return false; @@ -322,12 +309,9 @@ static INLINE bool d3d9_set_pixel_shader( } static INLINE bool d3d9_set_vertex_shader( - void *_dev, unsigned index, - void *data) + LPDIRECT3DDEVICE9 dev, unsigned index, + LPDIRECT3DVERTEXSHADER9 shader) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - LPDIRECT3DVERTEXSHADER9 shader = (LPDIRECT3DVERTEXSHADER9)data; - #ifdef _XBOX IDirect3DDevice9_SetVertexShader(dev, shader); #else @@ -338,12 +322,12 @@ static INLINE bool d3d9_set_vertex_shader( return true; } -static INLINE bool d3d9_set_vertex_shader_constantf(void *_dev, - UINT start_register,const float* constant_data, +static INLINE bool d3d9_set_vertex_shader_constantf( + LPDIRECT3DDEVICE9 dev, + UINT start_register, + const float* constant_data, unsigned vector4f_count) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - #ifdef _XBOX IDirect3DDevice9_SetVertexShaderConstantF(dev, start_register, constant_data, vector4f_count); @@ -356,13 +340,13 @@ static INLINE bool d3d9_set_vertex_shader_constantf(void *_dev, return true; } -static INLINE void d3d9_texture_blit(unsigned pixel_size, +static INLINE void d3d9_texture_blit( + unsigned pixel_size, void *tex, - void *_lr, const void *frame, + D3DLOCKED_RECT *lr, const void *frame, unsigned width, unsigned height, unsigned pitch) { unsigned y; - D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; for (y = 0; y < height; y++) { @@ -372,10 +356,10 @@ static INLINE void d3d9_texture_blit(unsigned pixel_size, } } -static INLINE bool d3d9_vertex_declaration_new(void *_dev, +static INLINE bool d3d9_vertex_declaration_new( + LPDIRECT3DDEVICE9 dev, const void *vertex_data, void **decl_data) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; const D3DVERTEXELEMENT9 *vertex_elements = (const D3DVERTEXELEMENT9*)vertex_data; LPDIRECT3DVERTEXDECLARATION9 **vertex_decl = (LPDIRECT3DVERTEXDECLARATION9**)decl_data; @@ -386,20 +370,18 @@ static INLINE bool d3d9_vertex_declaration_new(void *_dev, return false; } -static INLINE void d3d9_vertex_declaration_free(void *data) +static INLINE void d3d9_vertex_declaration_free( + LPDIRECT3DVERTEXDECLARATION9 decl) { - if (!data) - return; - - IDirect3DVertexDeclaration9_Release((LPDIRECT3DVERTEXDECLARATION9)data); + if (decl) + IDirect3DVertexDeclaration9_Release(decl); } -static INLINE void d3d9_set_viewports(void *_dev, void *_vp) +static INLINE void d3d9_set_viewports(LPDIRECT3DDEVICE9 dev, + void *vp) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - D3DVIEWPORT9 *vp = (D3DVIEWPORT9*)_vp; if (dev) - IDirect3DDevice9_SetViewport(dev, vp); + IDirect3DDevice9_SetViewport(dev, (D3DVIEWPORT9*)vp); } static INLINE void d3d9_set_render_state( @@ -408,73 +390,75 @@ static INLINE void d3d9_set_render_state( IDirect3DDevice9_SetRenderState(dev, state, value); } -static INLINE void d3d9_enable_blend_func(void *data) +static INLINE void d3d9_enable_blend_func(LPDIRECT3DDEVICE9 dev) { - if (!data) + if (!dev) return; - d3d9_set_render_state(data, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - d3d9_set_render_state(data, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - d3d9_set_render_state(data, D3DRS_ALPHABLENDENABLE, true); + d3d9_set_render_state(dev, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + d3d9_set_render_state(dev, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + d3d9_set_render_state(dev, D3DRS_ALPHABLENDENABLE, true); } -static INLINE void d3d9_disable_blend_func(void *data) +static INLINE void d3d9_disable_blend_func(LPDIRECT3DDEVICE9 dev) { - d3d9_set_render_state(data, D3DRS_ALPHABLENDENABLE, false); + if (dev) + d3d9_set_render_state(dev, D3DRS_ALPHABLENDENABLE, false); } static INLINE void -d3d9_set_vertex_declaration(void *data, void *vertex_data) +d3d9_set_vertex_declaration(LPDIRECT3DDEVICE9 dev, + LPDIRECT3DVERTEXDECLARATION9 vertex_data) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)data; if (dev) - IDirect3DDevice9_SetVertexDeclaration(dev, - (LPDIRECT3DVERTEXDECLARATION9)vertex_data); + IDirect3DDevice9_SetVertexDeclaration(dev, vertex_data); } -static INLINE void d3d9_set_texture_stage_state(void *_dev, - unsigned sampler, unsigned type, unsigned value) +static INLINE void d3d9_set_texture_stage_state( + LPDIRECT3DDEVICE9 dev, + unsigned sampler, + D3DTEXTURESTAGESTATETYPE type, + unsigned value) { #ifndef _XBOX /* XBox 360 has no fixed-function pipeline. */ - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (IDirect3DDevice9_SetTextureStageState(dev, sampler, - (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) + type, value) != D3D_OK) RARCH_ERR("SetTextureStageState call failed, sampler" ": %d, value: %d, type: %d\n", sampler, value, type); #endif } -static INLINE void d3d9_enable_alpha_blend_texture_func(void *data) +static INLINE void d3d9_enable_alpha_blend_texture_func(LPDIRECT3DDEVICE9 dev) { + if (!dev) + return; + /* Also blend the texture with the set alpha value. */ - d3d9_set_texture_stage_state(data, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); - d3d9_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); - d3d9_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); + d3d9_set_texture_stage_state(dev, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + d3d9_set_texture_stage_state(dev, 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + d3d9_set_texture_stage_state(dev, 0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); } void d3d9_frame_postprocess(void *data); -static INLINE void d3d9_surface_free(void *data) +static INLINE void d3d9_surface_free(LPDIRECT3DSURFACE9 surf) { - LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; - if (!surf) - return; - IDirect3DSurface9_Release(surf); + if (surf) + IDirect3DSurface9_Release(surf); } bool d3d9_device_get_render_target_data(void *dev, void *_src, void *_dst); -static INLINE bool d3d9_device_get_render_target(void *_dev, +static INLINE bool d3d9_device_get_render_target( + LPDIRECT3DDEVICE9 dev, unsigned idx, void **data) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev && SUCCEEDED(IDirect3DDevice9_GetRenderTarget(dev, idx, (LPDIRECT3DSURFACE9*)data))) return true; - return false; } @@ -518,41 +502,40 @@ bool d3d9_device_create_offscreen_plain_surface( void **surf_data, void *data); -static INLINE bool d3d9_surface_lock_rect(void *data, void *data2) +static INLINE bool d3d9_surface_lock_rect(LPDIRECT3DSURFACE9 surf, + D3DLOCKED_RECT *data2) { - LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; if (!surf) return false; #if defined(_XBOX) - IDirect3DSurface9_LockRect(surf, (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY); + IDirect3DSurface9_LockRect(surf, + data2, NULL, D3DLOCK_READONLY); #else if (FAILED(IDirect3DSurface9_LockRect(surf, - (D3DLOCKED_RECT*)data2, NULL, D3DLOCK_READONLY))) + data2, NULL, D3DLOCK_READONLY))) return false; #endif return true; } -static INLINE void d3d9_surface_unlock_rect(void *data) +static INLINE void d3d9_surface_unlock_rect(LPDIRECT3DSURFACE9 surf) { - LPDIRECT3DSURFACE9 surf = (LPDIRECT3DSURFACE9)data; - if (!surf) - return; - IDirect3DSurface9_UnlockRect(surf); + if (surf) + IDirect3DSurface9_UnlockRect(surf); } -void d3d9_surface_unlock_rect(void *data); static INLINE bool d3d9_get_adapter_display_mode( - void *_d3d, + LPDIRECT3D9 d3d, unsigned idx, void *display_mode) { - LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; if (!d3d) return false; #ifndef _XBOX - if (FAILED(IDirect3D9_GetAdapterDisplayMode(d3d, idx, (D3DDISPLAYMODE*)display_mode))) + if (FAILED( + IDirect3D9_GetAdapterDisplayMode( + d3d, idx, (D3DDISPLAYMODE*)display_mode))) return false; #endif @@ -567,11 +550,11 @@ bool d3d9_create_device(void *dev, bool d3d9_reset(void *dev, void *d3dpp); -static INLINE bool d3d9_device_get_backbuffer(void *_dev, +static INLINE bool d3d9_device_get_backbuffer( + LPDIRECT3DDEVICE9 dev, unsigned idx, unsigned swapchain_idx, unsigned backbuffer_type, void **data) { - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; if (dev && SUCCEEDED(IDirect3DDevice9_GetBackBuffer(dev, swapchain_idx, idx, @@ -589,13 +572,13 @@ bool d3d9_initialize_symbols(enum gfx_ctx_api api); void d3d9_deinitialize_symbols(void); -static INLINE bool d3d9_check_device_type(void *_d3d, +static INLINE bool d3d9_check_device_type( + LPDIRECT3D9 d3d, unsigned idx, INT32 disp_format, INT32 backbuffer_format, bool windowed_mode) { - LPDIRECT3D9 d3d = (LPDIRECT3D9)_d3d; if (d3d && SUCCEEDED(IDirect3D9_CheckDeviceType(d3d, 0, @@ -604,7 +587,6 @@ static INLINE bool d3d9_check_device_type(void *_d3d, (D3DFORMAT)backbuffer_format, windowed_mode))) return true; - return false; } From 2a655116a60c11a089e84c253bdfd3cdb78a7ca7 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 14:27:39 +0200 Subject: [PATCH 388/517] Set #define CINTERFACE in these files --- gfx/drivers/d3d8.c | 2 ++ gfx/drivers/d3d9.c | 2 ++ gfx/drivers_font/d3d_w32_font.c | 2 ++ gfx/drivers_renderchain/d3d9_cg_renderchain.c | 2 ++ gfx/drivers_renderchain/d3d9_hlsl_renderchain.c | 2 ++ gfx/drivers_shader/shader_hlsl.c | 2 ++ menu/drivers_display/menu_display_d3d8.c | 2 ++ menu/drivers_display/menu_display_d3d9.c | 2 ++ 8 files changed, 16 insertions(+) diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index 67e7aca4df..c614b97088 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -15,6 +15,8 @@ * If not, see . */ +#define CINTERFACE + #ifdef _XBOX #include #include diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index b33ef22a45..2d1b4a0447 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -15,6 +15,8 @@ * If not, see . */ +#define CINTERFACE + #ifdef _XBOX #include #include diff --git a/gfx/drivers_font/d3d_w32_font.c b/gfx/drivers_font/d3d_w32_font.c index 6d329cef82..3b2824e12e 100644 --- a/gfx/drivers_font/d3d_w32_font.c +++ b/gfx/drivers_font/d3d_w32_font.c @@ -14,6 +14,8 @@ * If not, see . */ +#define CINTERFACE + #include #include diff --git a/gfx/drivers_renderchain/d3d9_cg_renderchain.c b/gfx/drivers_renderchain/d3d9_cg_renderchain.c index d3406b3307..3dc5e9a366 100644 --- a/gfx/drivers_renderchain/d3d9_cg_renderchain.c +++ b/gfx/drivers_renderchain/d3d9_cg_renderchain.c @@ -14,6 +14,8 @@ * If not, see . */ +#define CINTERFACE + #include #include diff --git a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c index 8e5d2e7aac..ab3eaacc3e 100644 --- a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c +++ b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c @@ -14,6 +14,8 @@ * If not, see . */ +#define CINTERFACE + #include #include #include diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 1e86acc584..2b73a92f89 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -14,6 +14,8 @@ * If not, see . */ +#define CINTERFACE + #include #include #include diff --git a/menu/drivers_display/menu_display_d3d8.c b/menu/drivers_display/menu_display_d3d8.c index 208f6b566d..c71270d004 100644 --- a/menu/drivers_display/menu_display_d3d8.c +++ b/menu/drivers_display/menu_display_d3d8.c @@ -13,6 +13,8 @@ * If not, see . */ +#define CINTERFACE + #include #include diff --git a/menu/drivers_display/menu_display_d3d9.c b/menu/drivers_display/menu_display_d3d9.c index cb62c95972..cd83a8bce6 100644 --- a/menu/drivers_display/menu_display_d3d9.c +++ b/menu/drivers_display/menu_display_d3d9.c @@ -13,6 +13,8 @@ * If not, see . */ +#define CINTERFACE + #include #include From 9e741019f34b5d68ec9e56f01e61590836944fc1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 14:46:20 +0200 Subject: [PATCH 389/517] (menu_display_d3d8.c) Buildfix --- menu/drivers_display/menu_display_d3d8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu/drivers_display/menu_display_d3d8.c b/menu/drivers_display/menu_display_d3d8.c index c71270d004..3fc9ce0704 100644 --- a/menu/drivers_display/menu_display_d3d8.c +++ b/menu/drivers_display/menu_display_d3d8.c @@ -172,7 +172,7 @@ static void menu_display_d3d8_draw(void *data, video_frame_info_t *video_info) #if 1 if ((void*)draw->texture) { - D3DSURFACE_DESC desc; + D3D8SURFACE_DESC desc; if (d3d8_texture_get_level_desc((void*)draw->texture, 0, &desc)) { pv[i].u *= desc.Width; From 3a933778117bc8352ad2b48d9c8c05cec79f4759 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 14:55:15 +0200 Subject: [PATCH 390/517] D3D8 buildfix --- gfx/common/d3d8_common.c | 2 -- gfx/common/d3d8_common.h | 2 ++ menu/drivers_display/menu_display_d3d8.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gfx/common/d3d8_common.c b/gfx/common/d3d8_common.c index d2c8507ffb..5bbec85867 100644 --- a/gfx/common/d3d8_common.c +++ b/gfx/common/d3d8_common.c @@ -29,8 +29,6 @@ #include "../../configuration.h" #include "../../verbosity.h" -#include - #ifdef HAVE_D3DX #ifdef _XBOX #include diff --git a/gfx/common/d3d8_common.h b/gfx/common/d3d8_common.h index 41260b7340..890ba485f8 100644 --- a/gfx/common/d3d8_common.h +++ b/gfx/common/d3d8_common.h @@ -19,6 +19,8 @@ #include #include +#include + #include "../video_driver.h" RETRO_BEGIN_DECLS diff --git a/menu/drivers_display/menu_display_d3d8.c b/menu/drivers_display/menu_display_d3d8.c index 3fc9ce0704..c71270d004 100644 --- a/menu/drivers_display/menu_display_d3d8.c +++ b/menu/drivers_display/menu_display_d3d8.c @@ -172,7 +172,7 @@ static void menu_display_d3d8_draw(void *data, video_frame_info_t *video_info) #if 1 if ((void*)draw->texture) { - D3D8SURFACE_DESC desc; + D3DSURFACE_DESC desc; if (d3d8_texture_get_level_desc((void*)draw->texture, 0, &desc)) { pv[i].u *= desc.Width; From 88387ccbde97dc2af59300484ed83f742971192f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 15:34:08 +0200 Subject: [PATCH 391/517] (D3D8) Cleanups --- gfx/common/d3d8_common.c | 500 ++------------------------------------- gfx/common/d3d8_common.h | 476 +++++++++++++++++++++++++++++-------- 2 files changed, 391 insertions(+), 585 deletions(-) diff --git a/gfx/common/d3d8_common.c b/gfx/common/d3d8_common.c index 5bbec85867..f75d23b9fc 100644 --- a/gfx/common/d3d8_common.c +++ b/gfx/common/d3d8_common.c @@ -172,108 +172,32 @@ void d3d8_deinitialize_symbols(void) #endif } -bool d3d8_check_device_type(void *_d3d, - unsigned idx, - INT32 disp_format, - INT32 backbuffer_format, - bool windowed_mode) -{ - LPDIRECT3D8 d3d = (LPDIRECT3D8)_d3d; - if (d3d && - SUCCEEDED(IDirect3D8_CheckDeviceType(d3d, - 0, - D3DDEVTYPE_HAL, - disp_format, - backbuffer_format, - windowed_mode))) - return true; - - return false; -} - -bool d3d8_get_adapter_display_mode( - void *_d3d, - unsigned idx, - void *display_mode) -{ - LPDIRECT3D8 d3d = (LPDIRECT3D8)_d3d; - if (d3d && - SUCCEEDED(IDirect3D8_GetAdapterDisplayMode( - d3d, idx, (D3DDISPLAYMODE*)display_mode))) - return true; - - return false; -} - -bool d3d8_swap(void *data, void *_dev) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (IDirect3DDevice8_Present(dev, NULL, NULL, NULL, NULL) - == D3DERR_DEVICELOST) - return false; - return true; -} - -void d3d8_set_transform(void *_dev, - INT32 state, const void *_matrix) -{ - CONST D3DMATRIX *matrix = (CONST D3DMATRIX*)_matrix; - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - IDirect3DDevice8_SetTransform(dev, (D3DTRANSFORMSTATETYPE)state, matrix); -} - -bool d3d8_texture_get_level_desc(void *_tex, - unsigned idx, void *_ppsurface_level) -{ - LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; - if (SUCCEEDED(IDirect3DTexture8_GetLevelDesc(tex, idx, (D3DSURFACE_DESC*)_ppsurface_level))) - return true; - - return false; -} - -bool d3d8_texture_get_surface_level(void *_tex, - unsigned idx, void **_ppsurface_level) -{ - LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; - if (tex && - SUCCEEDED( - IDirect3DTexture8_GetSurfaceLevel( - tex, idx, (IDirect3DSurface8**)_ppsurface_level))) - return true; - return false; -} - #ifdef HAVE_D3DX static void *d3d8_texture_new_from_file( - void *dev, + LPDIRECT3DDEVICE8 dev, const char *path, unsigned width, unsigned height, unsigned miplevels, unsigned usage, D3DFORMAT format, - INT32 pool, unsigned filter, unsigned mipfilter, + D3DPOOL pool, unsigned filter, unsigned mipfilter, INT32 color_key, void *src_info_data, PALETTEENTRY *palette) { void *buf = NULL; - HRESULT hr = D3DCreateTextureFromFile((LPDIRECT3DDEVICE8)dev, + if (FAILED(D3DCreateTextureFromFile(dev, path, width, height, miplevels, usage, format, - (D3DPOOL)pool, filter, mipfilter, color_key, src_info_data, - palette, (struct IDirect3DTeture8**)&buf); - - if (FAILED(hr)) + pool, filter, mipfilter, color_key, src_info_data, + palette, (struct IDirect3DTeture8**)&buf))) return NULL; - return buf; } #endif -void *d3d8_texture_new(void *_dev, +void *d3d8_texture_new(LPDIRECT3DDEVICE8 dev, const char *path, unsigned width, unsigned height, unsigned miplevels, unsigned usage, INT32 format, INT32 pool, unsigned filter, unsigned mipfilter, INT32 color_key, void *src_info_data, PALETTEENTRY *palette, bool want_mipmap) { - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; void *buf = NULL; if (path) @@ -298,326 +222,6 @@ void *d3d8_texture_new(void *_dev, return buf; } -void d3d8_texture_free(void *_tex) -{ - LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; - if (!tex) - return; - IDirect3DTexture8_Release(tex); -} - -bool d3d8_surface_lock_rect(void *data, void *data2) -{ - LPDIRECT3DSURFACE8 surf = (LPDIRECT3DSURFACE8)data; - if (surf && - SUCCEEDED( - IDirect3DSurface8_LockRect( - surf, (D3DLOCKED_RECT*)data2, - NULL, D3DLOCK_READONLY))) - return true; - return false; -} - -void d3d8_surface_unlock_rect(void *data) -{ - LPDIRECT3DSURFACE8 surf = (LPDIRECT3DSURFACE8)data; - if (!surf) - return; - IDirect3DSurface8_UnlockRect(surf); -} - -void d3d8_surface_free(void *data) -{ - LPDIRECT3DSURFACE8 surf = (LPDIRECT3DSURFACE8)data; - if (!surf) - return; - IDirect3DSurface8_Release(surf); -} - -void *d3d8_vertex_buffer_new(void *_dev, - unsigned length, unsigned usage, - unsigned fvf, INT32 pool, void *handle) -{ - void *buf = NULL; - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (FAILED(IDirect3DDevice8_CreateVertexBuffer( - dev, length, usage, fvf, - (D3DPOOL)pool, - (struct IDirect3DVertexBuffer8**)&buf))) - return NULL; - - return buf; -} - -void d3d8_vertex_buffer_unlock(void *vertbuf_ptr) -{ - LPDIRECT3DVERTEXBUFFER8 vertbuf = (LPDIRECT3DVERTEXBUFFER8)vertbuf_ptr; - - if (!vertbuf) - return; - IDirect3DVertexBuffer8_Unlock(vertbuf); -} - -void *d3d8_vertex_buffer_lock(void *vertbuf_ptr) -{ - void *buf = NULL; - LPDIRECT3DVERTEXBUFFER8 vertbuf = (LPDIRECT3DVERTEXBUFFER8)vertbuf_ptr; - - if (!vertbuf) - return NULL; - - IDirect3DVertexBuffer8_Lock(vertbuf, 0, 0, (BYTE**)&buf, 0); - - if (!buf) - return NULL; - - return buf; -} - -void d3d8_vertex_buffer_free(void *vertex_data, void *vertex_declaration) -{ - if (vertex_data) - { - LPDIRECT3DVERTEXBUFFER8 buf = (LPDIRECT3DVERTEXBUFFER8)vertex_data; - IDirect3DVertexBuffer8_Release(buf); - buf = NULL; - } -} - -void d3d8_set_stream_source(void *_dev, unsigned stream_no, - void *stream_vertbuf_ptr, unsigned offset_bytes, - unsigned stride) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - LPDIRECT3DVERTEXBUFFER8 stream_vertbuf = (LPDIRECT3DVERTEXBUFFER8)stream_vertbuf_ptr; - if (!stream_vertbuf) - return; - IDirect3DDevice8_SetStreamSource(dev, stream_no, stream_vertbuf, stride); -} - -static void d3d8_set_texture_stage_state(void *_dev, - unsigned sampler, unsigned type, unsigned value) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (IDirect3DDevice8_SetTextureStageState(dev, sampler, (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) - RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); -} - -void d3d8_set_sampler_address_u(void *_dev, - unsigned sampler, unsigned value) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - d3d8_set_texture_stage_state(dev, sampler, D3DTSS_ADDRESSU, value); -} - -void d3d8_set_sampler_address_v(void *_dev, - unsigned sampler, unsigned value) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - d3d8_set_texture_stage_state(dev, sampler, D3DTSS_ADDRESSV, value); -} - -void d3d8_set_sampler_minfilter(void *_dev, - unsigned sampler, unsigned value) -{ - d3d8_set_texture_stage_state(_dev, sampler, D3DTSS_MINFILTER, value); -} - -void d3d8_set_sampler_magfilter(void *_dev, - unsigned sampler, unsigned value) -{ - d3d8_set_texture_stage_state(_dev, sampler, D3DTSS_MAGFILTER, value); -} - -bool d3d8_begin_scene(void *_dev) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (!dev) - return false; -#ifdef _XBOX - IDirect3DDevice8_BeginScene(dev); -#else - if (FAILED(IDirect3DDevice8_BeginScene(dev))) - return false; -#endif - - return true; -} - -void d3d8_end_scene(void *_dev) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (!dev) - return; - IDirect3DDevice8_EndScene(dev); -} - -static void d3d8_draw_primitive_internal(void *_dev, - D3DPRIMITIVETYPE type, unsigned start, unsigned count) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (!dev) - return; - IDirect3DDevice8_DrawPrimitive(dev, type, start, count); -} - -void d3d8_draw_primitive(void *dev, - INT32 type, unsigned start, unsigned count) -{ - if (!d3d8_begin_scene(dev)) - return; - - d3d8_draw_primitive_internal(dev, (D3DPRIMITIVETYPE)type, start, count); - d3d8_end_scene(dev); -} - -void d3d8_clear(void *_dev, - unsigned count, const void *rects, unsigned flags, - INT32 color, float z, unsigned stencil) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (!dev) - return; - IDirect3DDevice8_Clear(dev, count, (const D3DRECT*)rects, flags, - color, z, stencil); -} - -bool d3d8_device_get_render_target(void *_dev, - unsigned idx, void **data) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (dev && - SUCCEEDED(IDirect3DDevice8_GetRenderTarget(dev, - (LPDIRECT3DSURFACE8*)data))) - return true; - - return false; -} - - -bool d3d8_lock_rectangle(void *_tex, - unsigned level, void *_lr, RECT *rect, - unsigned rectangle_height, unsigned flags) -{ - D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; - LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; - if (tex && - IDirect3DTexture8_LockRect(tex, level, lr, rect, flags) == D3D_OK) - return true; - return false; -} - -void d3d8_unlock_rectangle(void *_tex) -{ - LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)_tex; - if (!tex) - return; - IDirect3DTexture8_UnlockRect(tex, 0); -} - -void d3d8_lock_rectangle_clear(void *tex, - unsigned level, void *_lr, RECT *rect, - unsigned rectangle_height, unsigned flags) -{ - D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; -#if defined(_XBOX) - level = 0; -#endif - memset(lr->pBits, level, rectangle_height * lr->Pitch); - d3d8_unlock_rectangle(tex); -} - -void d3d8_set_viewports(void *_dev, void *_vp) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - D3DVIEWPORT8 *vp = (D3DVIEWPORT8*)_vp; - if (!dev) - return; - IDirect3DDevice8_SetViewport(dev, vp); -} - -void d3d8_set_texture(void *_dev, unsigned sampler, - void *tex_data) -{ - LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)tex_data; - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (!dev || !tex) - return; - IDirect3DDevice8_SetTexture(dev, sampler, - (IDirect3DBaseTexture8*)tex); -} - -bool d3d8_set_vertex_shader(void *_dev, unsigned index, - void *data) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (IDirect3DDevice8_SetVertexShader(dev, index) != D3D_OK) - return false; - - return true; -} - -void d3d8_texture_blit(unsigned pixel_size, - void *tex, - void *_lr, const void *frame, - unsigned width, unsigned height, unsigned pitch) -{ - unsigned y; - D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; - - for (y = 0; y < height; y++) - { - const uint8_t *in = (const uint8_t*)frame + y * pitch; - uint8_t *out = (uint8_t*)lr->pBits + y * lr->Pitch; - memcpy(out, in, width * pixel_size); - } -} - -bool d3d8_get_render_state(void *data, INT32 state, DWORD *value) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; - if (dev && IDirect3DDevice8_GetRenderState(dev, (D3DRENDERSTATETYPE)state, value) == D3D_OK) - return true; - - return false; -} - -void d3d8_set_render_state(void *data, INT32 state, DWORD value) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; - if (!dev) - return; - IDirect3DDevice8_SetRenderState(dev, (D3DRENDERSTATETYPE)state, value); -} - -void d3d8_enable_blend_func(void *data) -{ - if (!data) - return; - - d3d8_set_render_state(data, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - d3d8_set_render_state(data, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - d3d8_set_render_state(data, D3DRS_ALPHABLENDENABLE, true); -} - -void d3d8_device_set_render_target(void *_dev, unsigned idx, - void *data) -{ - LPDIRECT3DSURFACE8 surf = (LPDIRECT3DSURFACE8)data; - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (!dev) - return; - IDirect3DDevice8_SetRenderTarget(dev, surf, NULL); -} - -void d3d8_enable_alpha_blend_texture_func(void *data) -{ - /* Also blend the texture with the set alpha value. */ - d3d8_set_texture_stage_state(data, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); - d3d8_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); - d3d8_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); -} - void d3d8_frame_postprocess(void *data) { #if defined(_XBOX) @@ -627,27 +231,19 @@ void d3d8_frame_postprocess(void *data) #endif } -void d3d8_disable_blend_func(void *data) -{ - d3d8_set_render_state(data, D3DRS_ALPHABLENDENABLE, false); -} - -static bool d3d8_reset_internal(void *data, +static bool d3d8_reset_internal(LPDIRECT3DDEVICE8 dev, D3DPRESENT_PARAMETERS *d3dpp ) { - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; if (dev && IDirect3DDevice8_Reset(dev, d3dpp) == D3D_OK) return true; - return false; } -static HRESULT d3d8_test_cooperative_level(void *data) +static HRESULT d3d8_test_cooperative_level(LPDIRECT3DDEVICE8 dev) { #ifndef _XBOX - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; if (dev) return IDirect3DDevice8_TestCooperativeLevel(dev); #endif @@ -655,15 +251,13 @@ static HRESULT d3d8_test_cooperative_level(void *data) } static bool d3d8_create_device_internal( - void *data, + LPDIRECT3DDEVICE8 dev, D3DPRESENT_PARAMETERS *d3dpp, - void *_d3d, + LPDIRECT3D8 d3d, HWND focus_window, unsigned cur_mon_id, DWORD behavior_flags) { - LPDIRECT3D8 d3d = (LPDIRECT3D8)_d3d; - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)data; if (dev && SUCCEEDED(IDirect3D8_CreateDevice(d3d, cur_mon_id, @@ -679,7 +273,7 @@ static bool d3d8_create_device_internal( bool d3d8_create_device(void *dev, void *d3dpp, - void *d3d, + LPDIRECT3D8 d3d, HWND focus_window, unsigned cur_mon_id) { @@ -732,32 +326,6 @@ bool d3d8_reset(void *dev, void *d3dpp) return false; } -bool d3d8_device_get_backbuffer(void *_dev, - unsigned idx, unsigned swapchain_idx, - unsigned backbuffer_type, void **data) -{ - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (dev && - SUCCEEDED(IDirect3DDevice8_GetBackBuffer(dev, idx, - (D3DBACKBUFFER_TYPE)backbuffer_type, - (LPDIRECT3DSURFACE8*)data))) - return true; - - return false; -} - - -void d3d8_device_free(void *_dev, void *_pd3d) -{ - LPDIRECT3D8 pd3d = (LPDIRECT3D8)_pd3d; - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; - if (dev) - IDirect3DDevice8_Release(dev); - - if (pd3d) - IDirect3D8_Release(pd3d); -} - INT32 d3d8_translate_filter(unsigned type) { switch (type) @@ -778,22 +346,22 @@ INT32 d3d8_translate_filter(unsigned type) return D3DTEXF_POINT; } -bool d3d8x_create_font_indirect(void *_dev, +bool d3d8x_create_font_indirect(LPDIRECT3DDEVICE8 dev, void *desc, void **font_data) { #ifdef HAVE_D3DX - LPDIRECT3DDEVICE8 dev = (LPDIRECT3DDEVICE8)_dev; if (SUCCEEDED(D3DCreateFontIndirect( dev, (CONST LOGFONT*)desc, (struct ID3DXFont**)font_data))) return true; #endif - return false; } -void d3d8x_font_draw_text(void *data, void *sprite_data, void *string_data, - unsigned count, void *rect_data, unsigned format, unsigned color) +void d3d8x_font_draw_text(void *data, + void *sprite_data, void *string_data, + unsigned count, void *rect_data, + unsigned format, unsigned color) { #ifdef HAVE_D3DX ID3DXFont *font = (ID3DXFont*)data; @@ -808,9 +376,8 @@ void d3d8x_font_release(void *data) { #ifdef HAVE_D3DX ID3DXFont *font = (ID3DXFont*)data; - if (!font) - return; - font->lpVtbl->Release(font); + if (font) + font->lpVtbl->Release(font); #endif } @@ -818,35 +385,8 @@ void d3d8x_font_get_text_metrics(void *data, void *metrics) { #ifdef HAVE_D3DX ID3DXFont *font = (ID3DXFont*)data; - if (!font) - return; - font->lpVtbl->GetTextMetrics(font, (TEXTMETRICA*)metrics); + if (font) + font->lpVtbl->GetTextMetrics(font, (TEXTMETRICA*)metrics); #endif } -INT32 d3d8_get_rgb565_format(void) -{ -#ifdef _XBOX - return D3DFMT_LIN_R5G6B5; -#else - return D3DFMT_R5G6B5; -#endif -} - -INT32 d3d8_get_argb8888_format(void) -{ -#ifdef _XBOX - return D3DFMT_LIN_A8R8G8B8; -#else - return D3DFMT_A8R8G8B8; -#endif -} - -INT32 d3d8_get_xrgb8888_format(void) -{ -#ifdef _XBOX - return D3DFMT_LIN_X8R8G8B8; -#else - return D3DFMT_X8R8G8B8; -#endif -} diff --git a/gfx/common/d3d8_common.h b/gfx/common/d3d8_common.h index 890ba485f8..7878076f86 100644 --- a/gfx/common/d3d8_common.h +++ b/gfx/common/d3d8_common.h @@ -18,168 +18,399 @@ #include #include +#include #include #include "../video_driver.h" +#include "../../verbosity.h" RETRO_BEGIN_DECLS -bool d3d8_swap(void *data, void *dev); +static INLINE bool d3d8_swap(void *data, LPDIRECT3DDEVICE8 dev) +{ + if (IDirect3DDevice8_Present(dev, NULL, NULL, NULL, NULL) + == D3DERR_DEVICELOST) + return false; + return true; +} -void *d3d8_vertex_buffer_new(void *dev, - unsigned length, unsigned usage, unsigned fvf, - INT32 pool, void *handle); +static INLINE void *d3d8_vertex_buffer_new( + LPDIRECT3DDEVICE8 dev, + unsigned length, unsigned usage, + unsigned fvf, D3DPOOL pool, void *handle) +{ + void *buf = NULL; + if (FAILED(IDirect3DDevice8_CreateVertexBuffer( + dev, length, usage, fvf, + pool, + (struct IDirect3DVertexBuffer8**)&buf))) + return NULL; + return buf; +} -void *d3d8_vertex_buffer_lock(void *data); -void d3d8_vertex_buffer_unlock(void *data); +static INLINE void * +d3d8_vertex_buffer_lock(LPDIRECT3DVERTEXBUFFER8 vertbuf) +{ + void *buf = NULL; -void d3d8_vertex_buffer_free(void *vertex_data, void *vertex_declaration); + if (!vertbuf) + return NULL; -bool d3d8_texture_get_level_desc(void *tex, - unsigned idx, void *_ppsurface_level); + IDirect3DVertexBuffer8_Lock(vertbuf, 0, 0, (BYTE**)&buf, 0); -bool d3d8_texture_get_surface_level(void *tex, - unsigned idx, void **_ppsurface_level); + if (!buf) + return NULL; -void *d3d8_texture_new(void *dev, + return buf; +} + +static INLINE void d3d8_vertex_buffer_unlock( + LPDIRECT3DVERTEXBUFFER8 vertbuf) +{ + if (vertbuf) + IDirect3DVertexBuffer8_Unlock(vertbuf); +} + +static INLINE void d3d8_vertex_buffer_free( + LPDIRECT3DVERTEXBUFFER8 buf, + void *vertex_declaration) +{ + if (buf) + { + IDirect3DVertexBuffer8_Release(buf); + buf = NULL; + } +} + +static INLINE bool d3d8_texture_get_level_desc( + LPDIRECT3DTEXTURE8 tex, + unsigned idx, void *_ppsurface_level) +{ + if (SUCCEEDED(IDirect3DTexture8_GetLevelDesc( + tex, idx, (D3DSURFACE_DESC*)_ppsurface_level))) + return true; + return false; +} + +static INLINE bool d3d8_texture_get_surface_level( + LPDIRECT3DTEXTURE8 tex, + unsigned idx, void **_ppsurface_level) +{ + if (tex && + SUCCEEDED( + IDirect3DTexture8_GetSurfaceLevel( + tex, idx, (IDirect3DSurface8**)_ppsurface_level))) + return true; + return false; +} + +void *d3d8_texture_new(LPDIRECT3DDEVICE8 dev, const char *path, unsigned width, unsigned height, unsigned miplevels, unsigned usage, INT32 format, INT32 pool, unsigned filter, unsigned mipfilter, - INT32 color_key, void *src_info, + INT32 color_key, void *src_info_data, PALETTEENTRY *palette, bool want_mipmap); -void d3d8_set_stream_source(void *dev, unsigned stream_no, - void *stream_vertbuf, unsigned offset_bytes, - unsigned stride); +static INLINE void d3d8_set_stream_source(LPDIRECT3DDEVICE8 dev, + unsigned stream_no, + LPDIRECT3DVERTEXBUFFER8 stream_vertbuf, + unsigned offset_bytes, + unsigned stride) +{ + if (stream_vertbuf) + IDirect3DDevice8_SetStreamSource(dev, + stream_no, stream_vertbuf, stride); +} -void d3d8_texture_free(void *tex); +static INLINE void d3d8_texture_free(LPDIRECT3DTEXTURE8 tex) +{ + if (tex) + IDirect3DTexture8_Release(tex); +} -void d3d8_set_transform(void *dev, - INT32 state, const void *_matrix); +static INLINE void d3d8_set_transform(LPDIRECT3DDEVICE8 dev, + D3DTRANSFORMSTATETYPE state, const D3DMATRIX *matrix) +{ + IDirect3DDevice8_SetTransform(dev, state, matrix); +} -void d3d8_set_sampler_address_u(void *dev, - unsigned sampler, unsigned value); +static INLINE void d3d8_set_texture_stage_state(LPDIRECT3DDEVICE8 dev, + unsigned sampler, D3DTEXTURESTAGESTATETYPE type, unsigned value) +{ + if (IDirect3DDevice8_SetTextureStageState(dev, sampler, + (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) + RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); +} -void d3d8_set_sampler_address_v(void *dev, - unsigned sampler, unsigned value); +static INLINE void d3d8_set_sampler_address_u(LPDIRECT3DDEVICE8 dev, + unsigned sampler, unsigned value) +{ + d3d8_set_texture_stage_state(dev, sampler, D3DTSS_ADDRESSU, value); +} -void d3d8_set_sampler_minfilter(void *dev, - unsigned sampler, unsigned value); +static INLINE void d3d8_set_sampler_address_v(LPDIRECT3DDEVICE8 dev, + unsigned sampler, unsigned value) +{ + d3d8_set_texture_stage_state(dev, sampler, D3DTSS_ADDRESSV, value); +} -void d3d8_set_sampler_magfilter(void *dev, - unsigned sampler, unsigned value); +static INLINE void d3d8_set_sampler_minfilter(void *_dev, + unsigned sampler, unsigned value) +{ + d3d8_set_texture_stage_state(_dev, sampler, D3DTSS_MINFILTER, value); +} + +static INLINE void d3d8_set_sampler_magfilter(void *_dev, + unsigned sampler, unsigned value) +{ + d3d8_set_texture_stage_state(_dev, sampler, D3DTSS_MAGFILTER, value); +} void d3d8_set_sampler_mipfilter(void *dev, unsigned sampler, unsigned value); -bool d3d8_begin_scene(void *dev); +static INLINE bool d3d8_begin_scene(LPDIRECT3DDEVICE8 dev) +{ + if (!dev) + return false; +#ifdef _XBOX + IDirect3DDevice8_BeginScene(dev); +#else + if (FAILED(IDirect3DDevice8_BeginScene(dev))) + return false; +#endif -void d3d8_end_scene(void *dev); + return true; +} -void d3d8_draw_primitive(void *dev, - INT32 type, unsigned start, unsigned count); +static INLINE void d3d8_end_scene(LPDIRECT3DDEVICE8 dev) +{ + if (dev) + IDirect3DDevice8_EndScene(dev); +} -void d3d8_clear(void *dev, +static INLINE void d3d8_draw_primitive(LPDIRECT3DDEVICE8 dev, + D3DPRIMITIVETYPE type, unsigned start, unsigned count) +{ + if (!d3d8_begin_scene(dev)) + return; + + IDirect3DDevice8_DrawPrimitive(dev, type, start, count); + d3d8_end_scene(dev); +} + +static INLINE void d3d8_clear(LPDIRECT3DDEVICE8 dev, unsigned count, const void *rects, unsigned flags, - INT32 color, float z, unsigned stencil); + INT32 color, float z, unsigned stencil) +{ + if (dev) + IDirect3DDevice8_Clear(dev, count, (const D3DRECT*)rects, flags, + color, z, stencil); +} -bool d3d8_lock_rectangle(void *tex, - unsigned level, void *lock_rect, RECT *rect, - unsigned rectangle_height, unsigned flags); +static INLINE bool d3d8_lock_rectangle( + LPDIRECT3DTEXTURE8 tex, + unsigned level, D3DLOCKED_RECT *lr, RECT *rect, + unsigned rectangle_height, unsigned flags) +{ + if (tex && + IDirect3DTexture8_LockRect(tex, + level, lr, rect, flags) == D3D_OK) + return true; + return false; +} -void d3d8_lock_rectangle_clear(void *tex, - unsigned level, void *lock_rect, RECT *rect, - unsigned rectangle_height, unsigned flags); +static INLINE void d3d8_unlock_rectangle(LPDIRECT3DTEXTURE8 tex) +{ + if (tex) + IDirect3DTexture8_UnlockRect(tex, 0); +} -void d3d8_unlock_rectangle(void *tex); - -void d3d8_set_texture(void *dev, unsigned sampler, - void *tex_data); - -bool d3d8_create_vertex_shader(void *dev, - const DWORD *a, void **b); - -bool d3d8_create_pixel_shader(void *dev, - const DWORD *a, void **b); - -void d3d8_free_vertex_shader(void *dev, void *data); - -bool d3d8_set_vertex_shader(void *dev, unsigned index, - void *data); - -void d3d8_texture_blit(unsigned pixel_size, +static INLINE void d3d8_lock_rectangle_clear( void *tex, - void *lr, const void *frame, - unsigned width, unsigned height, unsigned pitch); + unsigned level, void *_lr, RECT *rect, + unsigned rectangle_height, unsigned flags) +{ + D3DLOCKED_RECT *lr = (D3DLOCKED_RECT*)_lr; +#if defined(_XBOX) + level = 0; +#endif + memset(lr->pBits, level, rectangle_height * lr->Pitch); + d3d8_unlock_rectangle(tex); +} -bool d3d8_vertex_declaration_new(void *dev, - const void *vertex_data, void **decl_data); +static INLINE void d3d8_set_texture( + LPDIRECT3DDEVICE8 dev, unsigned sampler, + LPDIRECT3DTEXTURE8 tex) +{ + if (dev && tex) + IDirect3DDevice8_SetTexture(dev, sampler, + (IDirect3DBaseTexture8*)tex); +} -void d3d8_vertex_declaration_free(void *data); +static INLINE bool d3d8_set_vertex_shader( + LPDIRECT3DDEVICE8 dev, + unsigned index, + void *data) +{ + if (IDirect3DDevice8_SetVertexShader(dev, index) != D3D_OK) + return false; + return true; +} -void d3d8_set_viewports(void *dev, void *vp); +static INLINE void d3d8_texture_blit( + unsigned pixel_size, + void *tex, + D3DLOCKED_RECT *lr, + const void *frame, + unsigned width, unsigned height, unsigned pitch) +{ + unsigned y; -void d3d8_enable_blend_func(void *data); + for (y = 0; y < height; y++) + { + const uint8_t *in = (const uint8_t*)frame + y * pitch; + uint8_t *out = (uint8_t*)lr->pBits + y * lr->Pitch; + memcpy(out, in, width * pixel_size); + } +} -void d3d8_disable_blend_func(void *data); +static INLINE void d3d8_set_viewports( + LPDIRECT3DDEVICE8 dev, + void *vp) +{ + if (dev) + IDirect3DDevice8_SetViewport(dev, (D3DVIEWPORT8*)vp); +} -void d3d8_set_vertex_declaration(void *data, void *vertex_data); +static INLINE void d3d8_set_render_state( + LPDIRECT3DDEVICE8 dev, + D3DRENDERSTATETYPE state, + DWORD value) +{ + if (dev) + IDirect3DDevice8_SetRenderState(dev, state, value); +} -void d3d8_enable_alpha_blend_texture_func(void *data); +static INLINE void d3d8_enable_blend_func(void *data) +{ + if (!data) + return; + + d3d8_set_render_state(data, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + d3d8_set_render_state(data, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + d3d8_set_render_state(data, D3DRS_ALPHABLENDENABLE, true); +} + +static INLINE void d3d8_disable_blend_func(void *data) +{ + d3d8_set_render_state(data, D3DRS_ALPHABLENDENABLE, false); +} + +static INLINE void d3d8_enable_alpha_blend_texture_func(void *data) +{ + /* Also blend the texture with the set alpha value. */ + d3d8_set_texture_stage_state(data, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + d3d8_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + d3d8_set_texture_stage_state(data, 0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); +} void d3d8_frame_postprocess(void *data); -void d3d8_surface_free(void *data); +static INLINE void d3d8_surface_free(LPDIRECT3DSURFACE8 surf) +{ + if (surf) + IDirect3DSurface8_Release(surf); +} -bool d3d8_device_get_render_target_data(void *dev, - void *_src, void *_dst); +static INLINE bool d3d8_device_get_render_target( + LPDIRECT3DDEVICE8 dev, + unsigned idx, void **data) +{ + if (dev && + SUCCEEDED(IDirect3DDevice8_GetRenderTarget(dev, + (LPDIRECT3DSURFACE8*)data))) + return true; + return false; +} -bool d3d8_device_get_render_target(void *dev, - unsigned idx, void **data); +static INLINE void d3d8_device_set_render_target( + LPDIRECT3DDEVICE8 dev, unsigned idx, + LPDIRECT3DSURFACE8 surf) +{ + if (dev) + IDirect3DDevice8_SetRenderTarget(dev, surf, NULL); +} -void d3d8_device_set_render_target(void *dev, unsigned idx, - void *data); +static INLINE bool d3d8_get_render_state(LPDIRECT3DDEVICE8 dev, + D3DRENDERSTATETYPE state, DWORD *value) +{ + if (dev && + IDirect3DDevice8_GetRenderState(dev, state, value) == D3D_OK) + return true; + return false; +} -bool d3d8_get_render_state(void *data, - INT32 state, DWORD *value); +static INLINE bool d3d8_surface_lock_rect( + LPDIRECT3DSURFACE8 surf, void *data2) +{ + if (surf && + SUCCEEDED( + IDirect3DSurface8_LockRect( + surf, (D3DLOCKED_RECT*)data2, + NULL, D3DLOCK_READONLY))) + return true; + return false; +} -void d3d8_set_render_state(void *data, - INT32 state, DWORD value); +static INLINE void d3d8_surface_unlock_rect(LPDIRECT3DSURFACE8 surf) +{ + if (surf) + IDirect3DSurface8_UnlockRect(surf); +} -void d3d8_device_set_render_target(void *dev, unsigned idx, - void *data); - -bool d3d8_device_create_offscreen_plain_surface( - void *dev, - unsigned width, - unsigned height, - unsigned format, - unsigned pool, - void **surf_data, - void *data); - -bool d3d8_surface_lock_rect(void *data, void *data2); - -void d3d8_surface_unlock_rect(void *data); - -bool d3d8_get_adapter_display_mode(void *d3d, +static INLINE bool d3d8_get_adapter_display_mode( + LPDIRECT3D8 d3d, unsigned idx, - void *display_mode); + void *display_mode) +{ + if (d3d && + SUCCEEDED(IDirect3D8_GetAdapterDisplayMode( + d3d, idx, (D3DDISPLAYMODE*)display_mode))) + return true; + return false; +} bool d3d8_create_device(void *dev, void *d3dpp, - void *d3d, + LPDIRECT3D8 d3d, HWND focus_window, unsigned cur_mon_id); bool d3d8_reset(void *dev, void *d3dpp); -bool d3d8_device_get_backbuffer(void *dev, +static INLINE bool d3d8_device_get_backbuffer( + LPDIRECT3DDEVICE8 dev, unsigned idx, unsigned swapchain_idx, - unsigned backbuffer_type, void **data); + unsigned backbuffer_type, void **data) +{ + if (dev && + SUCCEEDED(IDirect3DDevice8_GetBackBuffer(dev, idx, + (D3DBACKBUFFER_TYPE)backbuffer_type, + (LPDIRECT3DSURFACE8*)data))) + return true; + return false; +} -void d3d8_device_free(void *dev, void *pd3d); +static INLINE void d3d8_device_free( + LPDIRECT3DDEVICE8 dev, LPDIRECT3D8 pd3d) +{ + if (dev) + IDirect3DDevice8_Release(dev); + if (pd3d) + IDirect3D8_Release(pd3d); +} void *d3d8_create(void); @@ -187,13 +418,25 @@ bool d3d8_initialize_symbols(enum gfx_ctx_api api); void d3d8_deinitialize_symbols(void); -bool d3d8_check_device_type(void *d3d, +static INLINE bool d3d8_check_device_type( + LPDIRECT3D8 d3d, unsigned idx, INT32 disp_format, INT32 backbuffer_format, - bool windowed_mode); + bool windowed_mode) +{ + if (d3d && + SUCCEEDED(IDirect3D8_CheckDeviceType(d3d, + 0, + D3DDEVTYPE_HAL, + disp_format, + backbuffer_format, + windowed_mode))) + return true; + return false; +} -bool d3d8x_create_font_indirect(void *dev, +bool d3d8x_create_font_indirect(LPDIRECT3DDEVICE8 dev, void *desc, void **font_data); void d3d8x_font_draw_text(void *data, void *sprite_data, void *string_data, @@ -205,9 +448,32 @@ void d3d8x_font_release(void *data); INT32 d3d8_translate_filter(unsigned type); -INT32 d3d8_get_rgb565_format(void); -INT32 d3d8_get_argb8888_format(void); -INT32 d3d8_get_xrgb8888_format(void); +static INLINE INT32 d3d8_get_rgb565_format(void) +{ +#ifdef _XBOX + return D3DFMT_LIN_R5G6B5; +#else + return D3DFMT_R5G6B5; +#endif +} + +static INLINE INT32 d3d8_get_argb8888_format(void) +{ +#ifdef _XBOX + return D3DFMT_LIN_A8R8G8B8; +#else + return D3DFMT_A8R8G8B8; +#endif +} + +static INLINE INT32 d3d8_get_xrgb8888_format(void) +{ +#ifdef _XBOX + return D3DFMT_LIN_X8R8G8B8; +#else + return D3DFMT_X8R8G8B8; +#endif +} RETRO_END_DECLS From 04fa4871f0b2b4c758a92f3835e90b6d9e6ab543 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 15:45:11 +0200 Subject: [PATCH 392/517] Create d3d_translate_filter and get rid of duplicate functions --- gfx/common/d3d8_common.c | 21 ---------------- gfx/common/d3d8_common.h | 2 -- gfx/common/d3d9_common.c | 20 ---------------- gfx/common/d3d9_common.h | 2 -- gfx/common/d3d_common.c | 20 ++++++++++++++++ gfx/common/d3d_common.h | 2 ++ gfx/drivers_renderchain/d3d9_cg_renderchain.c | 24 +++++++++---------- 7 files changed, 34 insertions(+), 57 deletions(-) diff --git a/gfx/common/d3d8_common.c b/gfx/common/d3d8_common.c index f75d23b9fc..1b88aefa68 100644 --- a/gfx/common/d3d8_common.c +++ b/gfx/common/d3d8_common.c @@ -26,7 +26,6 @@ #include #endif -#include "../../configuration.h" #include "../../verbosity.h" #ifdef HAVE_D3DX @@ -326,26 +325,6 @@ bool d3d8_reset(void *dev, void *d3dpp) return false; } -INT32 d3d8_translate_filter(unsigned type) -{ - switch (type) - { - case RARCH_FILTER_UNSPEC: - { - settings_t *settings = config_get_ptr(); - if (!settings->bools.video_smooth) - break; - } - /* fall-through */ - case RARCH_FILTER_LINEAR: - return D3DTEXF_LINEAR; - case RARCH_FILTER_NEAREST: - break; - } - - return D3DTEXF_POINT; -} - bool d3d8x_create_font_indirect(LPDIRECT3DDEVICE8 dev, void *desc, void **font_data) { diff --git a/gfx/common/d3d8_common.h b/gfx/common/d3d8_common.h index 7878076f86..8bb9f8580f 100644 --- a/gfx/common/d3d8_common.h +++ b/gfx/common/d3d8_common.h @@ -446,8 +446,6 @@ void d3d8x_font_get_text_metrics(void *data, void *metrics); void d3d8x_font_release(void *data); -INT32 d3d8_translate_filter(unsigned type); - static INLINE INT32 d3d8_get_rgb565_format(void) { #ifdef _XBOX diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index 962fd1a02b..e0b6bc1edf 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -489,26 +489,6 @@ void d3d9_device_free(void *_dev, void *_pd3d) IDirect3D9_Release(pd3d); } -INT32 d3d9_translate_filter(unsigned type) -{ - switch (type) - { - case RARCH_FILTER_UNSPEC: - { - settings_t *settings = config_get_ptr(); - if (!settings->bools.video_smooth) - break; - } - /* fall-through */ - case RARCH_FILTER_LINEAR: - return D3DTEXF_LINEAR; - case RARCH_FILTER_NEAREST: - break; - } - - return D3DTEXF_POINT; -} - bool d3d9x_create_font_indirect(void *_dev, void *desc, void **font_data) { diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index 9ccc5e6517..6709636932 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -602,8 +602,6 @@ void d3dxbuffer_release(void *data); void d3d9x_font_release(void *data); -INT32 d3d9_translate_filter(unsigned type); - bool d3d9x_compile_shader( const char *src, unsigned src_data_len, diff --git a/gfx/common/d3d_common.c b/gfx/common/d3d_common.c index 5b5dc616a7..020f20d681 100644 --- a/gfx/common/d3d_common.c +++ b/gfx/common/d3d_common.c @@ -129,3 +129,23 @@ void *d3d_matrix_rotation_z(void *_pout, float angle) pout->m[1][0] = -sin(angle); return pout; } + +int32_t d3d_translate_filter(unsigned type) +{ + switch (type) + { + case RARCH_FILTER_UNSPEC: + { + settings_t *settings = config_get_ptr(); + if (!settings->bools.video_smooth) + break; + } + /* fall-through */ + case RARCH_FILTER_LINEAR: + return (int32_t)D3DTEXF_LINEAR; + case RARCH_FILTER_NEAREST: + break; + } + + return (int32_t)D3DTEXF_POINT; +} diff --git a/gfx/common/d3d_common.h b/gfx/common/d3d_common.h index e34809f1f4..b951881ae2 100644 --- a/gfx/common/d3d_common.h +++ b/gfx/common/d3d_common.h @@ -52,6 +52,8 @@ void *d3d_matrix_multiply(void *_pout, void *d3d_matrix_rotation_z(void *_pout, float angle); +int32_t d3d_translate_filter(unsigned type); + RETRO_END_DECLS #endif diff --git a/gfx/drivers_renderchain/d3d9_cg_renderchain.c b/gfx/drivers_renderchain/d3d9_cg_renderchain.c index 3dc5e9a366..353fca3ab2 100644 --- a/gfx/drivers_renderchain/d3d9_cg_renderchain.c +++ b/gfx/drivers_renderchain/d3d9_cg_renderchain.c @@ -465,9 +465,9 @@ static void d3d9_cg_renderchain_bind_orig(cg_renderchain_t *chain, index = cgGetParameterResourceIndex(param); d3d9_set_texture(chain->dev, index, chain->passes->data[0].tex); d3d9_set_sampler_magfilter(chain->dev, index, - d3d9_translate_filter(chain->passes->data[0].info.pass->filter)); + d3d_translate_filter(chain->passes->data[0].info.pass->filter)); d3d9_set_sampler_minfilter(chain->dev, index, - d3d9_translate_filter(chain->passes->data[0].info.pass->filter)); + d3d_translate_filter(chain->passes->data[0].info.pass->filter)); d3d9_set_sampler_address_u(chain->dev, index, D3DTADDRESS_BORDER); d3d9_set_sampler_address_v(chain->dev, index, D3DTADDRESS_BORDER); unsigned_vector_list_append(chain->bound_tex, index); @@ -541,9 +541,9 @@ static void d3d9_cg_renderchain_bind_prev(void *data, const void *pass_data) unsigned_vector_list_append(chain->bound_tex, index); d3d9_set_sampler_magfilter(chain->dev, index, - d3d9_translate_filter(chain->passes->data[0].info.pass->filter)); + d3d_translate_filter(chain->passes->data[0].info.pass->filter)); d3d9_set_sampler_minfilter(chain->dev, index, - d3d9_translate_filter(chain->passes->data[0].info.pass->filter)); + d3d_translate_filter(chain->passes->data[0].info.pass->filter)); d3d9_set_sampler_address_u(chain->dev, index, D3DTADDRESS_BORDER); d3d9_set_sampler_address_v(chain->dev, index, D3DTADDRESS_BORDER); } @@ -572,9 +572,9 @@ static void d3d9_cg_renderchain_add_lut_internal(void *data, d3d9_set_texture(chain->dev, index, chain->luts->data[i].tex); d3d9_set_sampler_magfilter(chain->dev, index, - d3d9_translate_filter(chain->luts->data[i].smooth ? RARCH_FILTER_LINEAR : RARCH_FILTER_NEAREST)); + d3d_translate_filter(chain->luts->data[i].smooth ? RARCH_FILTER_LINEAR : RARCH_FILTER_NEAREST)); d3d9_set_sampler_minfilter(chain->dev, index, - d3d9_translate_filter(chain->luts->data[i].smooth ? RARCH_FILTER_LINEAR : RARCH_FILTER_NEAREST)); + d3d_translate_filter(chain->luts->data[i].smooth ? RARCH_FILTER_LINEAR : RARCH_FILTER_NEAREST)); d3d9_set_sampler_address_u(chain->dev, index, D3DTADDRESS_BORDER); d3d9_set_sampler_address_v(chain->dev, index, D3DTADDRESS_BORDER); unsigned_vector_list_append(chain->bound_tex, index); @@ -625,9 +625,9 @@ static void d3d9_cg_renderchain_bind_pass( d3d9_set_texture(chain->dev, index, chain->passes->data[i].tex); d3d9_set_sampler_magfilter(chain->dev, index, - d3d9_translate_filter(chain->passes->data[i].info.pass->filter)); + d3d_translate_filter(chain->passes->data[i].info.pass->filter)); d3d9_set_sampler_minfilter(chain->dev, index, - d3d9_translate_filter(chain->passes->data[i].info.pass->filter)); + d3d_translate_filter(chain->passes->data[i].info.pass->filter)); d3d9_set_sampler_address_u(chain->dev, index, D3DTADDRESS_BORDER); d3d9_set_sampler_address_v(chain->dev, index, D3DTADDRESS_BORDER); } @@ -889,9 +889,9 @@ static bool d3d9_cg_renderchain_create_first_pass( d3d9_set_texture(chain->dev, 0, chain->prev.tex[i]); d3d9_set_sampler_minfilter(chain->dev, 0, - d3d9_translate_filter(info->pass->filter)); + d3d_translate_filter(info->pass->filter)); d3d9_set_sampler_magfilter(chain->dev, 0, - d3d9_translate_filter(info->pass->filter)); + d3d_translate_filter(info->pass->filter)); d3d9_set_sampler_address_u(chain->dev, 0, D3DTADDRESS_BORDER); d3d9_set_sampler_address_v(chain->dev, 0, D3DTADDRESS_BORDER); d3d9_set_texture(chain->dev, 0, NULL); @@ -1428,9 +1428,9 @@ static void cg_d3d9_renderchain_render_pass( d3d9_set_texture(chain->dev, 0, pass->tex); d3d9_set_sampler_minfilter(chain->dev, 0, - d3d9_translate_filter(pass->info.pass->filter)); + d3d_translate_filter(pass->info.pass->filter)); d3d9_set_sampler_magfilter(chain->dev, 0, - d3d9_translate_filter(pass->info.pass->filter)); + d3d_translate_filter(pass->info.pass->filter)); d3d9_set_vertex_declaration(chain->dev, pass->vertex_decl); for (i = 0; i < 4; i++) From 5dc916f0b392a91482562185d1dda594b675e3ba Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 15:45:56 +0200 Subject: [PATCH 393/517] Take out another header include --- gfx/common/d3d9_common.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index e0b6bc1edf..cc0ae1739a 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -26,7 +26,6 @@ #include #endif -#include "../../configuration.h" #include "../../verbosity.h" #include "d3d9_common.h" From 1fd2cf1ff5545c2c1727b00d59432a9ac5638027 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 17:43:57 +0200 Subject: [PATCH 394/517] Update libchdr chd --- libretro-common/formats/libchdr/libchdr_chd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libretro-common/formats/libchdr/libchdr_chd.c b/libretro-common/formats/libchdr/libchdr_chd.c index bb8e91de11..b1413a156f 100644 --- a/libretro-common/formats/libchdr/libchdr_chd.c +++ b/libretro-common/formats/libchdr/libchdr_chd.c @@ -50,6 +50,7 @@ #include #include #include + #include #define TRUE 1 @@ -467,7 +468,7 @@ chd_error lzma_codec_init(void* codec, uint32_t hunkbytes) { CLzmaEncProps encoder_props; CLzmaEncHandle enc; - Byte decoder_props[LZMA_PROPS_SIZE]; + uint8_t decoder_props[LZMA_PROPS_SIZE]; lzma_allocator* alloc; size_t props_size; lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; From 7bef8746a35b740334e00366a722b6f47451aeb3 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 18:04:45 +0200 Subject: [PATCH 395/517] Add chd_precache code to libchdr --- libretro-common/formats/libchdr/libchdr_chd.c | 115 +++++++++++++----- libretro-common/include/libchdr/chd.h | 3 + 2 files changed, 88 insertions(+), 30 deletions(-) diff --git a/libretro-common/formats/libchdr/libchdr_chd.c b/libretro-common/formats/libchdr/libchdr_chd.c index b1413a156f..af726e9ae4 100644 --- a/libretro-common/formats/libchdr/libchdr_chd.c +++ b/libretro-common/formats/libchdr/libchdr_chd.c @@ -282,6 +282,7 @@ struct _chd_file #ifdef NEED_CACHE_HUNK UINT32 maxhunk; /* maximum hunk accessed */ #endif + UINT8 * file_cache; /* cache of underlying file */ }; /*************************************************************************** @@ -1530,6 +1531,32 @@ cleanup: return err; } +chd_error chd_precache(chd_file *chd) +{ + ssize_t size, count; + + if (!chd->file_cache) + { + core_fseek(chd->file, 0, SEEK_END); + size = core_ftell(chd->file); + if (size <= 0) + return CHDERR_INVALID_DATA; + chd->file_cache = malloc(size); + if (chd->file_cache == NULL) + return CHDERR_OUT_OF_MEMORY; + core_fseek(chd->file, 0, SEEK_SET); + count = core_fread(chd->file, chd->file_cache, size); + if (count != size) + { + free(chd->file_cache); + chd->file_cache = NULL; + return CHDERR_READ_ERROR; + } + } + + return CHDERR_NONE; +} + /*------------------------------------------------- chd_close - close a CHD file for access -------------------------------------------------*/ @@ -1602,6 +1629,9 @@ void chd_close(chd_file *chd) if (PRINTF_MAX_HUNK) printf("Max hunk = %d/%d\n", chd->maxhunk, chd->header.totalhunks); #endif + if (chd->file_cache) + free(chd->file_cache); + /* free our memory */ free(chd); } @@ -2037,6 +2067,35 @@ static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum) } #endif +static UINT8* read_compressed(chd_file *chd, UINT64 offset, size_t size) +{ + ssize_t bytes; + if (chd->file_cache) + return chd->file_cache + offset; + core_fseek(chd->file, offset, SEEK_SET); + bytes = core_fread(chd->file, chd->compressed, size); + if (bytes != size) + return NULL; + return chd->compressed; +} + +static chd_error read_uncompressed(chd_file *chd, UINT64 offset, size_t size, UINT8 *dest) +{ + ssize_t bytes; + if (chd->file_cache) + { + memcpy(dest, chd->file_cache + offset, size); + return CHDERR_NONE; + } + core_fseek(chd->file, offset, SEEK_SET); + bytes = core_fread(chd->file, dest, size); + if (bytes != size) + return CHDERR_READ_ERROR; + return CHDERR_NONE; +} + + + /*------------------------------------------------- hunk_read_into_memory - read a hunk into memory at the given location @@ -2068,30 +2127,28 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des { /* compressed data */ case V34_MAP_ENTRY_TYPE_COMPRESSED: + { + void *codec; + UINT8 *bytes = read_compressed(chd, entry->offset, + entry->length); + if (bytes == NULL) + return CHDERR_READ_ERROR; - /* read it into the decompression buffer */ - if (core_fseek(chd->file, entry->offset, SEEK_SET) != 0) - return CHDERR_READ_ERROR; - bytes = core_fread(chd->file, chd->compressed, entry->length); - if (bytes != entry->length) - return CHDERR_READ_ERROR; - - /* now decompress using the codec */ - err = CHDERR_NONE; - codec = &chd->zlib_codec_data; - if (chd->codecintf[0]->decompress != NULL) - err = (*chd->codecintf[0]->decompress)(codec, chd->compressed, entry->length, dest, chd->header.hunkbytes); - if (err != CHDERR_NONE) - return err; + /* now decompress using the codec */ + err = CHDERR_NONE; + codec = &chd->zlib_codec_data; + if (chd->codecintf[0]->decompress != NULL) + err = (*chd->codecintf[0]->decompress)(codec, chd->compressed, entry->length, dest, chd->header.hunkbytes); + if (err != CHDERR_NONE) + return err; + } break; /* uncompressed data */ case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: - if (core_fseek(chd->file, entry->offset, SEEK_SET) != 0) - return CHDERR_READ_ERROR; - bytes = core_fread(chd->file, dest, chd->header.hunkbytes); - if (bytes != chd->header.hunkbytes) - return CHDERR_READ_ERROR; + err = read_uncompressed(chd, entry->offset, chd->header.hunkbytes, dest); + if (err != CHDERR_NONE) + return err; break; /* mini-compressed data */ @@ -2128,6 +2185,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des uint16_t blockcrc; #endif uint8_t *rawmap = &chd->header.rawmap[chd->header.mapentrybytes * hunknum]; + UINT8 *bytes; #if 0 /* uncompressed case - TODO */ @@ -2158,11 +2216,9 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des case COMPRESSION_TYPE_1: case COMPRESSION_TYPE_2: case COMPRESSION_TYPE_3: - if (core_fseek(chd->file, blockoffs, SEEK_SET) != 0) - return CHDERR_READ_ERROR; - if(core_fread(chd->file, chd->compressed, blocklen) != blocklen) - return CHDERR_READ_ERROR; - + bytes = read_compressed(chd, blockoffs, blocklen); + if (bytes == NULL) + return CHDERR_READ_ERROR; switch (chd->codecintf[rawmap[0]]->compression) { case CHD_CODEC_CD_LZMA: @@ -2189,13 +2245,12 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des return CHDERR_NONE; case COMPRESSION_NONE: - if (core_fseek(chd->file, blockoffs, SEEK_SET) != 0) - return CHDERR_READ_ERROR; - if (core_fread(chd->file, dest, chd->header.hunkbytes) != chd->header.hunkbytes) - return CHDERR_READ_ERROR; + err = read_uncompressed(chd, blockoffs, blocklen, dest); + if (err != CHDERR_NONE) + return err; #ifdef VERIFY_BLOCK_CRC - if (crc16(dest, chd->header.hunkbytes) != blockcrc) - return CHDERR_DECOMPRESSION_ERROR; + if (crc16(dest, chd->header.hunkbytes) != blockcrc) + return CHDERR_DECOMPRESSION_ERROR; #endif return CHDERR_NONE; diff --git a/libretro-common/include/libchdr/chd.h b/libretro-common/include/libchdr/chd.h index 1a33e0cc32..87759ca133 100644 --- a/libretro-common/include/libchdr/chd.h +++ b/libretro-common/include/libchdr/chd.h @@ -348,8 +348,11 @@ struct _chd_verify_result /* open an existing CHD file */ chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd); + chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd); +/* precache underlying file */ +chd_error chd_precache(chd_file *chd); /* close a CHD file */ void chd_close(chd_file *chd); From 8a1f2f5cf2a30c80efea3ba199358f427d5e3822 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 18:36:30 +0200 Subject: [PATCH 396/517] Update libretro-common --- libretro-common/formats/libchdr/libchdr_chd.c | 94 ++++++++++--------- .../formats/libchdr/libchdr_flac.c | 3 +- libretro-common/include/libchdr/chd.h | 5 +- libretro-common/include/libchdr/coretypes.h | 10 +- 4 files changed, 55 insertions(+), 57 deletions(-) diff --git a/libretro-common/formats/libchdr/libchdr_chd.c b/libretro-common/formats/libchdr/libchdr_chd.c index af726e9ae4..93e4fbc039 100644 --- a/libretro-common/formats/libchdr/libchdr_chd.c +++ b/libretro-common/formats/libchdr/libchdr_chd.c @@ -52,6 +52,7 @@ #include #include +#include #define TRUE 1 #define FALSE 0 @@ -255,7 +256,7 @@ struct _chd_file { UINT32 cookie; /* cookie, should equal COOKIE_VALUE */ - core_file * file; /* handle to the open core file */ + RFILE * file; /* handle to the open core file */ UINT8 owns_file; /* flag indicating if this file should be closed on chd_close() */ chd_header header; /* header, extracted from file */ @@ -1150,8 +1151,8 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header) } /* read the reader */ - core_fseek(chd->file, header->mapoffset, SEEK_SET); - core_fread(chd->file, rawbuf, sizeof(rawbuf)); + filestream_seek(chd->file, header->mapoffset, SEEK_SET); + filestream_read(chd->file, rawbuf, sizeof(rawbuf)); mapbytes = get_bigendian_uint32(&rawbuf[0]); firstoffs = get_bigendian_uint48(&rawbuf[4]); mapcrc = get_bigendian_uint16(&rawbuf[10]); @@ -1164,8 +1165,8 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header) if (compressed == NULL) return CHDERR_OUT_OF_MEMORY; - core_fseek(chd->file, header->mapoffset + 16, SEEK_SET); - core_fread(chd->file, compressed, mapbytes); + filestream_seek(chd->file, header->mapoffset + 16, SEEK_SET); + filestream_read(chd->file, compressed, mapbytes); bitbuf = create_bitstream(compressed, sizeof(uint8_t) * mapbytes); if (bitbuf == NULL) { @@ -1317,7 +1318,7 @@ static INLINE void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 h chd_open_file - open a CHD file for access -------------------------------------------------*/ -chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd) +chd_error chd_open_file(RFILE *file, int mode, chd_file *parent, chd_file **chd) { chd_file *newchd = NULL; chd_error err; @@ -1409,13 +1410,13 @@ chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file ** /* find the codec interface */ if (newchd->header.version < 5) { - for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + for (intfnum = 0; intfnum < ARRAY_SIZE(codec_interfaces); intfnum++) if (codec_interfaces[intfnum].compression == newchd->header.compression[0]) { newchd->codecintf[0] = &codec_interfaces[intfnum]; break; } - if (intfnum == ARRAY_LENGTH(codec_interfaces)) + if (intfnum == ARRAY_SIZE(codec_interfaces)) EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT); /* initialize the codec */ @@ -1429,9 +1430,9 @@ chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file ** { int i, decompnum; /* verify the compression types and initialize the codecs */ - for (decompnum = 0; decompnum < ARRAY_LENGTH(newchd->header.compression); decompnum++) + for (decompnum = 0; decompnum < ARRAY_SIZE(newchd->header.compression); decompnum++) { - for (i = 0 ; i < ARRAY_LENGTH(codec_interfaces) ; i++) + for (i = 0 ; i < ARRAY_SIZE(codec_interfaces) ; i++) { if (codec_interfaces[i].compression == newchd->header.compression[decompnum]) { @@ -1496,7 +1497,7 @@ cleanup: chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd) { chd_error err; - core_file *file = NULL; + RFILE *file = NULL; /* choose the proper mode */ switch(mode) @@ -1510,8 +1511,11 @@ chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file ** } /* open the file */ - file = core_fopen(filename); - if (file == 0) + file = filestream_open(filename, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (!file) { err = CHDERR_FILE_NOT_FOUND; goto cleanup; @@ -1527,25 +1531,25 @@ chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file ** cleanup: if ((err != CHDERR_NONE) && (file != NULL)) - core_fclose(file); + filestream_close(file); return err; } chd_error chd_precache(chd_file *chd) { - ssize_t size, count; + int64_t size, count; if (!chd->file_cache) { - core_fseek(chd->file, 0, SEEK_END); - size = core_ftell(chd->file); + filestream_seek(chd->file, 0, SEEK_END); + size = filestream_tell(chd->file); if (size <= 0) return CHDERR_INVALID_DATA; chd->file_cache = malloc(size); if (chd->file_cache == NULL) return CHDERR_OUT_OF_MEMORY; - core_fseek(chd->file, 0, SEEK_SET); - count = core_fread(chd->file, chd->file_cache, size); + filestream_seek(chd->file, 0, SEEK_SET); + count = filestream_read(chd->file, chd->file_cache, size); if (count != size) { free(chd->file_cache); @@ -1623,7 +1627,7 @@ void chd_close(chd_file *chd) /* close the file */ if (chd->owns_file && chd->file != NULL) - core_fclose(chd->file); + filestream_close(chd->file); #ifdef NEED_CACHE_HUNK if (PRINTF_MAX_HUNK) printf("Max hunk = %d/%d\n", chd->maxhunk, chd->header.totalhunks); @@ -1641,7 +1645,7 @@ void chd_close(chd_file *chd) core_file -------------------------------------------------*/ -core_file *chd_core_file(chd_file *chd) +RFILE *chd_core_file(chd_file *chd) { return chd->file; } @@ -1768,8 +1772,8 @@ chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, /* read the metadata */ outputlen = MIN(outputlen, metaentry.length); - core_fseek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET); - count = core_fread(chd->file, output, outputlen); + filestream_seek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET); + count = filestream_read(chd->file, output, outputlen); if (count != outputlen) return CHDERR_READ_ERROR; @@ -1840,11 +1844,11 @@ static chd_error header_validate(const chd_header *header) return CHDERR_INVALID_PARAMETER; /* require a supported compression mechanism */ - for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + for (intfnum = 0; intfnum < ARRAY_SIZE(codec_interfaces); intfnum++) if (codec_interfaces[intfnum].compression == header->compression[0]) break; - if (intfnum == ARRAY_LENGTH(codec_interfaces)) + if (intfnum == ARRAY_SIZE(codec_interfaces)) return CHDERR_INVALID_PARAMETER; /* require a valid hunksize */ @@ -1919,8 +1923,8 @@ static chd_error header_read(chd_file *chd, chd_header *header) return CHDERR_INVALID_FILE; /* seek and read */ - core_fseek(chd->file, 0, SEEK_SET); - count = core_fread(chd->file, rawheader, sizeof(rawheader)); + filestream_seek(chd->file, 0, SEEK_SET); + count = filestream_read(chd->file, rawheader, sizeof(rawheader)); if (count != sizeof(rawheader)) return CHDERR_READ_ERROR; @@ -2069,11 +2073,11 @@ static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum) static UINT8* read_compressed(chd_file *chd, UINT64 offset, size_t size) { - ssize_t bytes; + int64_t bytes; if (chd->file_cache) return chd->file_cache + offset; - core_fseek(chd->file, offset, SEEK_SET); - bytes = core_fread(chd->file, chd->compressed, size); + filestream_seek(chd->file, offset, SEEK_SET); + bytes = filestream_read(chd->file, chd->compressed, size); if (bytes != size) return NULL; return chd->compressed; @@ -2081,14 +2085,14 @@ static UINT8* read_compressed(chd_file *chd, UINT64 offset, size_t size) static chd_error read_uncompressed(chd_file *chd, UINT64 offset, size_t size, UINT8 *dest) { - ssize_t bytes; + int64_t bytes; if (chd->file_cache) { memcpy(dest, chd->file_cache + offset, size); return CHDERR_NONE; } - core_fseek(chd->file, offset, SEEK_SET); - bytes = core_fread(chd->file, dest, size); + filestream_seek(chd->file, offset, SEEK_SET); + bytes = filestream_read(chd->file, dest, size); if (bytes != size) return CHDERR_READ_ERROR; return CHDERR_NONE; @@ -2277,12 +2281,12 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des INTERNAL MAP ACCESS ***************************************************************************/ -static size_t core_fsize(core_file *f) +static size_t core_fsize(RFILE *f) { - long rv,p = ftell(f); - fseek(f, 0, SEEK_END); - rv = ftell(f); - fseek(f, p, SEEK_SET); + int64_t rv, p = filestream_tell(f); + filestream_seek(f, 0, SEEK_END); + rv = filestream_tell(f); + filestream_seek(f, p, SEEK_SET); return rv; } @@ -2315,8 +2319,8 @@ static chd_error map_read(chd_file *chd) entries = MAP_STACK_ENTRIES; /* read that many */ - core_fseek(chd->file, fileoffset, SEEK_SET); - count = core_fread(chd->file, raw_map_entries, entries * entrysize); + filestream_seek(chd->file, fileoffset, SEEK_SET); + count = filestream_read(chd->file, raw_map_entries, entries * entrysize); if (count != entries * entrysize) { err = CHDERR_READ_ERROR; @@ -2344,8 +2348,8 @@ static chd_error map_read(chd_file *chd) } /* verify the cookie */ - core_fseek(chd->file, fileoffset, SEEK_SET); - count = core_fread(chd->file, &cookie, entrysize); + filestream_seek(chd->file, fileoffset, SEEK_SET); + count = filestream_read(chd->file, &cookie, entrysize); if (count != entrysize || memcmp(&cookie, END_OF_LIST_COOKIE, entrysize)) { err = CHDERR_INVALID_FILE; @@ -2385,11 +2389,11 @@ static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metai while (metaentry->offset != 0) { UINT8 raw_meta_header[METADATA_HEADER_SIZE]; - UINT32 count; + int64_t count; /* read the raw header */ - core_fseek(chd->file, metaentry->offset, SEEK_SET); - count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header)); + filestream_seek(chd->file, metaentry->offset, SEEK_SET); + count = filestream_read(chd->file, raw_meta_header, sizeof(raw_meta_header)); if (count != sizeof(raw_meta_header)) break; diff --git a/libretro-common/formats/libchdr/libchdr_flac.c b/libretro-common/formats/libchdr/libchdr_flac.c index 8fe0cf7752..4cc5dc20a4 100644 --- a/libretro-common/formats/libchdr/libchdr_flac.c +++ b/libretro-common/formats/libchdr/libchdr_flac.c @@ -12,6 +12,7 @@ #include #include #include +#include /*************************************************************************** * FLAC DECODER @@ -153,7 +154,7 @@ bool flac_decoder::decode(int16_t **samples, uint32_t num_samples, bool swap_end { /* make sure we don't have too many channels */ int chans = channels(); - if (chans > ARRAY_LENGTH(m_uncompressed_start)) + if (chans > ARRAY_SIZE(m_uncompressed_start)) return false; /* configure the uncompressed buffer */ diff --git a/libretro-common/include/libchdr/chd.h b/libretro-common/include/libchdr/chd.h index 87759ca133..5180d8e3e9 100644 --- a/libretro-common/include/libchdr/chd.h +++ b/libretro-common/include/libchdr/chd.h @@ -47,6 +47,7 @@ extern "C" { #endif #include "coretypes.h" +#include /*************************************************************************** @@ -347,7 +348,7 @@ struct _chd_verify_result /* chd_error chd_create_file(core_file *file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); */ /* open an existing CHD file */ -chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd); +chd_error chd_open_file(RFILE *file, int mode, chd_file *parent, chd_file **chd); chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd); @@ -358,7 +359,7 @@ chd_error chd_precache(chd_file *chd); void chd_close(chd_file *chd); /* return the associated core_file */ -core_file *chd_core_file(chd_file *chd); +RFILE *chd_core_file(chd_file *chd); /* return an error string for the given CHD error */ const char *chd_error_string(chd_error err); diff --git a/libretro-common/include/libchdr/coretypes.h b/libretro-common/include/libchdr/coretypes.h index 5a769f6a86..2a68a8a4c3 100644 --- a/libretro-common/include/libchdr/coretypes.h +++ b/libretro-common/include/libchdr/coretypes.h @@ -3,8 +3,7 @@ #include #include - -#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0])) +#include typedef uint64_t UINT64; #ifndef OSD_CPU_H @@ -20,11 +19,4 @@ typedef int16_t INT16; typedef int8_t INT8; #endif -#define core_file FILE -#define core_fopen(file) fopen(file, "rb") -#define core_fseek fseek -#define core_fread(fc, buff, len) fread(buff, 1, len, fc) -#define core_fclose fclose -#define core_ftell ftell - #endif From 8e2dfa618418436d2aaba2c1cdd63ec9538e3a95 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 19:19:27 +0200 Subject: [PATCH 397/517] Cleanups --- libretro-common/formats/libchdr/libchdr_chd.c | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/libretro-common/formats/libchdr/libchdr_chd.c b/libretro-common/formats/libchdr/libchdr_chd.c index 93e4fbc039..6e3ee38a2a 100644 --- a/libretro-common/formats/libchdr/libchdr_chd.c +++ b/libretro-common/formats/libchdr/libchdr_chd.c @@ -74,6 +74,8 @@ #define OLD_MAP_ENTRY_SIZE 8 /* V1-V2 */ #define METADATA_HEADER_SIZE 16 /* metadata header size */ +#define CRCMAP_HASH_SIZE 4095 /* number of CRC hashtable entries */ + #define MAP_ENTRY_FLAG_TYPE_MASK 0x0f /* what type of hunk */ #define MAP_ENTRY_FLAG_NO_CRC 0x10 /* no CRC is present */ @@ -86,22 +88,18 @@ #define NO_MATCH (~0) +#define MAP_ENTRY_TYPE_INVALID 0x0000 /* invalid type */ +#define MAP_ENTRY_TYPE_COMPRESSED 0x0001 /* standard compression */ +#define MAP_ENTRY_TYPE_UNCOMPRESSED 0x0002 /* uncompressed data */ +#define MAP_ENTRY_TYPE_MINI 0x0003 /* mini: use offset as raw data */ +#define MAP_ENTRY_TYPE_SELF_HUNK 0x0004 /* same as another hunk in this file */ +#define MAP_ENTRY_TYPE_PARENT_HUNK 0x0005 /* same as a hunk in the parent file */ +#define MAP_ENTRY_TYPE_2ND_COMPRESSED 0x0006 /* compressed with secondary algorithm (usually FLAC CDDA) */ + #ifdef WANT_RAW_DATA_SECTOR static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 }; #endif -/* V3-V4 entry types */ -enum -{ - V34_MAP_ENTRY_TYPE_INVALID = 0, /* invalid type */ - V34_MAP_ENTRY_TYPE_COMPRESSED = 1, /* standard compression */ - V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, /* uncompressed data */ - V34_MAP_ENTRY_TYPE_MINI = 3, /* mini: use offset as raw data */ - V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, /* same as another hunk in this file */ - V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, /* same as a hunk in the parent file */ - V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 /* compressed with secondary algorithm (usually FLAC CDDA) */ -}; - /* V5 compression types */ enum { @@ -142,6 +140,8 @@ enum MACROS ***************************************************************************/ +#define SET_ERROR_AND_CLEANUP(err) do { last_error = (err); goto cleanup; } while (0) + #define EARLY_EXIT(x) do { (void)(x); goto cleanup; } while (0) /*************************************************************************** @@ -1302,7 +1302,7 @@ static INLINE void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 h entry->offset = get_bigendian_uint64(&base[0]); entry->crc = 0; entry->length = entry->offset >> 44; - entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? V34_MAP_ENTRY_TYPE_UNCOMPRESSED : V34_MAP_ENTRY_TYPE_COMPRESSED); + entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? MAP_ENTRY_TYPE_UNCOMPRESSED : MAP_ENTRY_TYPE_COMPRESSED); #ifdef __MWERKS__ entry->offset = entry->offset & 0x00000FFFFFFFFFFFLL; #else @@ -2130,7 +2130,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des switch (entry->flags & MAP_ENTRY_FLAG_TYPE_MASK) { /* compressed data */ - case V34_MAP_ENTRY_TYPE_COMPRESSED: + case MAP_ENTRY_TYPE_COMPRESSED: { void *codec; UINT8 *bytes = read_compressed(chd, entry->offset, @@ -2149,21 +2149,21 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des break; /* uncompressed data */ - case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + case MAP_ENTRY_TYPE_UNCOMPRESSED: err = read_uncompressed(chd, entry->offset, chd->header.hunkbytes, dest); if (err != CHDERR_NONE) return err; break; /* mini-compressed data */ - case V34_MAP_ENTRY_TYPE_MINI: + case MAP_ENTRY_TYPE_MINI: put_bigendian_uint64(&dest[0], entry->offset); for (bytes = 8; bytes < chd->header.hunkbytes; bytes++) dest[bytes] = dest[bytes - 8]; break; /* self-referenced data */ - case V34_MAP_ENTRY_TYPE_SELF_HUNK: + case MAP_ENTRY_TYPE_SELF_HUNK: #ifdef NEED_CACHE_HUNK if (chd->cachehunk == entry->offset && dest == chd->cache) break; @@ -2171,7 +2171,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des return hunk_read_into_memory(chd, entry->offset, dest); /* parent-referenced data */ - case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + case MAP_ENTRY_TYPE_PARENT_HUNK: err = hunk_read_into_memory(chd->parent, entry->offset, dest); if (err != CHDERR_NONE) return err; @@ -2342,8 +2342,8 @@ static chd_error map_read(chd_file *chd) /* track the maximum offset */ for (j = 0; j < entries; j++) - if ((chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_COMPRESSED || - (chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_UNCOMPRESSED) + if ((chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == MAP_ENTRY_TYPE_COMPRESSED || + (chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == MAP_ENTRY_TYPE_UNCOMPRESSED) maxoffset = MAX(maxoffset, chd->map[i + j].offset + chd->map[i + j].length); } From fc169cf4fe5fb7ddb5120aae073d88e76d7895b4 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 19:50:14 +0200 Subject: [PATCH 398/517] Split up libchdr_chd.c into several files --- Makefile.common | 13 + libretro-common/formats/libchdr/libchdr_chd.c | 638 +----------------- .../formats/libchdr/libchdr_lzma.c | 356 ++++++++++ .../formats/libchdr/libchdr_zlib.c | 300 ++++++++ libretro-common/include/libchdr/chd.h | 2 + .../include/libchdr/libchdr_zlib.h | 68 ++ libretro-common/include/libchdr/lzma.h | 72 ++ 7 files changed, 826 insertions(+), 623 deletions(-) create mode 100644 libretro-common/formats/libchdr/libchdr_lzma.c create mode 100644 libretro-common/formats/libchdr/libchdr_zlib.c create mode 100644 libretro-common/include/libchdr/libchdr_zlib.h create mode 100644 libretro-common/include/libchdr/lzma.h diff --git a/Makefile.common b/Makefile.common index 44e6b5652a..ef5f2b7a4d 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1498,6 +1498,7 @@ endif ifeq ($(HAVE_FLAC), 1) ifeq ($(HAVE_7ZIP), 1) ifeq ($(HAVE_ZLIB), 1) + HAVE_CHD = 1 DEFINES += -DHAVE_CHD -DWANT_SUBCODE -DWANT_RAW_DATA_SECTOR CFLAGS += -I$(LIBRETRO_COMM_DIR)/formats/libchdr OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_bitstream.o \ @@ -1510,6 +1511,18 @@ endif endif endif +ifeq ($(HAVE_CHD), 1) +ifeq ($(HAVE_7ZIP), 1) + OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_lzma.o +endif +endif + +ifeq ($(HAVE_CHD), 1) +ifeq ($(HAVE_ZLIB), 1) + OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_zlib.o +endif +endif + ifeq ($(HAVE_RTGA), 1) DEFINES += -DHAVE_RTGA OBJ += $(LIBRETRO_COMM_DIR)/formats/tga/rtga.o diff --git a/libretro-common/formats/libchdr/libchdr_chd.c b/libretro-common/formats/libchdr/libchdr_chd.c index 6e3ee38a2a..3712c926dd 100644 --- a/libretro-common/formats/libchdr/libchdr_chd.c +++ b/libretro-common/formats/libchdr/libchdr_chd.c @@ -47,9 +47,12 @@ #include #include #include -#include -#include -#include +#ifdef HAVE_7ZIP +#include +#endif +#ifdef HAVE_ZLIB +#include +#endif #include #include @@ -82,7 +85,6 @@ #define CHD_V1_SECTOR_SIZE 512 /* size of a "sector" in the V1 header */ #define COOKIE_VALUE 0xbaadf00d -#define MAX_ZLIB_ALLOCS 64 #define END_OF_LIST_COOKIE "EndOfListCookie" @@ -97,7 +99,7 @@ #define MAP_ENTRY_TYPE_2ND_COMPRESSED 0x0006 /* compressed with secondary algorithm (usually FLAC CDDA) */ #ifdef WANT_RAW_DATA_SECTOR -static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 }; +const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 }; #endif /* V5 compression types */ @@ -183,62 +185,6 @@ struct _metadata_entry UINT8 flags; /* flag bits */ }; -/* codec-private data for the ZLIB codec */ - -typedef struct _zlib_allocator zlib_allocator; -struct _zlib_allocator -{ - UINT32 * allocptr[MAX_ZLIB_ALLOCS]; -}; - -typedef struct _zlib_codec_data zlib_codec_data; -struct _zlib_codec_data -{ - z_stream inflater; - zlib_allocator allocator; -}; - -/* codec-private data for the LZMA codec */ -#define MAX_LZMA_ALLOCS 64 - -typedef struct _lzma_allocator lzma_allocator; -struct _lzma_allocator -{ - void *(*Alloc)(void *p, size_t size); - void (*Free)(void *p, void *address); /* address can be 0 */ - void (*FreeSz)(void *p, void *address, size_t size); /* address can be 0 */ - uint32_t* allocptr[MAX_LZMA_ALLOCS]; -}; - -typedef struct _lzma_codec_data lzma_codec_data; -struct _lzma_codec_data -{ - CLzmaDec decoder; - lzma_allocator allocator; -}; - -/* codec-private data for the CDZL codec */ -typedef struct _cdzl_codec_data cdzl_codec_data; -struct _cdzl_codec_data { - /* internal state */ - zlib_codec_data base_decompressor; -#ifdef WANT_SUBCODE - zlib_codec_data subcode_decompressor; -#endif - uint8_t* buffer; -}; - -/* codec-private data for the CDLZ codec */ -typedef struct _cdlz_codec_data cdlz_codec_data; -struct _cdlz_codec_data { - /* internal state */ - lzma_codec_data base_decompressor; -#ifdef WANT_SUBCODE - zlib_codec_data subcode_decompressor; -#endif - uint8_t* buffer; -}; - /* codec-private data for the CDFL codec */ typedef struct _cdfl_codec_data cdfl_codec_data; struct _cdfl_codec_data { @@ -275,9 +221,13 @@ struct _chd_file UINT8 * compressed; /* pointer to buffer for compressed data */ const codec_interface * codecintf[4]; /* interface to the codec */ +#ifdef HAVE_ZLIB zlib_codec_data zlib_codec_data; /* zlib codec data */ cdzl_codec_data cdzl_codec_data; /* cdzl codec data */ +#endif +#ifdef HAVE_7ZIP cdlz_codec_data cdlz_codec_data; /* cdlz codec data */ +#endif cdfl_codec_data cdfl_codec_data; /* cdfl codec data */ #ifdef NEED_CACHE_HUNK @@ -313,418 +263,12 @@ static chd_error map_read(chd_file *chd); /* metadata management */ static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry); -/* zlib compression codec */ -static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes); -static void zlib_codec_free(void *codec); -static chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); -static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size); -static void zlib_fast_free(voidpf opaque, voidpf address); - -/* lzma compression codec */ -static chd_error lzma_codec_init(void *codec, uint32_t hunkbytes); -static void lzma_codec_free(void *codec); -static chd_error lzma_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); - -/* cdzl compression codec */ -static chd_error cdzl_codec_init(void* codec, uint32_t hunkbytes); -static void cdzl_codec_free(void* codec); -static chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); - -/* cdlz compression codec */ -static chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes); -static void cdlz_codec_free(void* codec); -static chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); /* cdfl compression codec */ static chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes); static void cdfl_codec_free(void* codec); static chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); -/*************************************************************************** - * LZMA ALLOCATOR HELPER - *************************************************************************** - */ - -void *lzma_fast_alloc(void *p, size_t size); -void lzma_fast_free(void *p, void *address); - -/*------------------------------------------------- - * lzma_allocator_init - *------------------------------------------------- - */ - -void lzma_allocator_init(void* p) -{ - lzma_allocator *codec = (lzma_allocator *)(p); - - /* reset pointer list */ - memset(codec->allocptr, 0, sizeof(codec->allocptr)); - codec->Alloc = lzma_fast_alloc; - codec->Free = lzma_fast_free; -} - -/*------------------------------------------------- - * lzma_allocator_free - *------------------------------------------------- - */ - -void lzma_allocator_free(void* p ) -{ - lzma_allocator *codec = (lzma_allocator *)(p); - - /* free our memory */ - int i; - for (i = 0 ; i < MAX_LZMA_ALLOCS ; i++) - { - if (codec->allocptr[i] != NULL) - free(codec->allocptr[i]); - } -} - -/*------------------------------------------------- - * lzma_fast_alloc - fast malloc for lzma, which - * allocates and frees memory frequently - *------------------------------------------------- - */ - -void *lzma_fast_alloc(void *p, size_t size) -{ - int scan; - uint32_t *addr = NULL; - lzma_allocator *codec = (lzma_allocator *)(p); - - /* compute the size, rounding to the nearest 1k */ - size = (size + 0x3ff) & ~0x3ff; - - /* reuse a hunk if we can */ - for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) - { - uint32_t *ptr = codec->allocptr[scan]; - if (ptr != NULL && size == *ptr) - { - /* set the low bit of the size so we don't match next time */ - *ptr |= 1; - return ptr + 1; - } - } - - /* alloc a new one and put it into the list */ - addr = (uint32_t *)malloc(sizeof(uint32_t) * (size + sizeof(uint32_t))); - if (!addr) - return NULL; - - for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) - { - if (codec->allocptr[scan] == NULL) - { - codec->allocptr[scan] = addr; - break; - } - } - - /* set the low bit of the size so we don't match next time */ - *addr = size | 1; - return addr + 1; -} - -/*------------------------------------------------- - * lzma_fast_free - fast free for lzma, which - * allocates and frees memory frequently - *------------------------------------------------- - */ - -void lzma_fast_free(void *p, void *address) -{ - int scan; - uint32_t *ptr; - lzma_allocator *codec; - if (address == NULL) - return; - - codec = (lzma_allocator *)(p); - - /* find the hunk */ - ptr = (uint32_t *)(address) - 1; - for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) - { - if (ptr == codec->allocptr[scan]) - { - /* clear the low bit of the size to allow matches */ - *ptr &= ~1; - return; - } - } -} - -/*************************************************************************** - * LZMA DECOMPRESSOR - *************************************************************************** - */ - -/*------------------------------------------------- - * lzma_codec_init - constructor - *------------------------------------------------- - */ - -chd_error lzma_codec_init(void* codec, uint32_t hunkbytes) -{ - CLzmaEncProps encoder_props; - CLzmaEncHandle enc; - uint8_t decoder_props[LZMA_PROPS_SIZE]; - lzma_allocator* alloc; - size_t props_size; - lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; - - /* construct the decoder */ - LzmaDec_Construct(&lzma_codec->decoder); - - /* FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK - * This code assumes that the current version of the encoder imposes the same requirements on the - * decoder as the encoder used to produce the file. This is not necessarily true. The format - * needs to be changed so the encoder properties are written to the file. - - * configure the properties like the compressor did */ - LzmaEncProps_Init(&encoder_props); - encoder_props.level = 9; - encoder_props.reduceSize = hunkbytes; - LzmaEncProps_Normalize(&encoder_props); - - /* convert to decoder properties */ - alloc = &lzma_codec->allocator; - lzma_allocator_init(alloc); - enc = LzmaEnc_Create((ISzAlloc*)alloc); - if (!enc) - return CHDERR_DECOMPRESSION_ERROR; - if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK) - { - LzmaEnc_Destroy(enc, (ISzAlloc*)&alloc, (ISzAlloc*)&alloc); - return CHDERR_DECOMPRESSION_ERROR; - } - props_size = sizeof(decoder_props); - if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK) - { - LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc); - return CHDERR_DECOMPRESSION_ERROR; - } - LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc); - - /* do memory allocations */ - if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, (ISzAlloc*)alloc) != SZ_OK) - return CHDERR_DECOMPRESSION_ERROR; - - /* Okay */ - return CHDERR_NONE; -} - -/*------------------------------------------------- - * lzma_codec_free - *------------------------------------------------- - */ - -void lzma_codec_free(void* codec) -{ - lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; - lzma_allocator* alloc = &lzma_codec->allocator; - - /* free memory */ - lzma_allocator_free(alloc); - LzmaDec_Free(&lzma_codec->decoder, (ISzAlloc*)&lzma_codec->allocator); -} - -/*------------------------------------------------- - * decompress - decompress data using the LZMA - * codec - *------------------------------------------------- - */ - -chd_error lzma_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) -{ - ELzmaStatus status; - SRes res; - size_t consumedlen, decodedlen; - /* initialize */ - lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; - LzmaDec_Init(&lzma_codec->decoder); - - /* decode */ - consumedlen = complen; - decodedlen = destlen; - res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status); - if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen) - return CHDERR_DECOMPRESSION_ERROR; - return CHDERR_NONE; -} - -/* cdlz */ -chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes) -{ - chd_error ret; - cdlz_codec_data* cdlz = (cdlz_codec_data*) codec; - - /* allocate buffer */ - cdlz->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); - if (cdlz->buffer == NULL) - return CHDERR_OUT_OF_MEMORY; - - ret = lzma_codec_init(&cdlz->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); - if (ret != CHDERR_NONE) - return ret; - -#ifdef WANT_SUBCODE - ret = zlib_codec_init(&cdlz->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); - if (ret != CHDERR_NONE) - return ret; -#endif - - return CHDERR_NONE; -} - -void cdlz_codec_free(void* codec) -{ - cdlz_codec_data* cdlz = (cdlz_codec_data*) codec; - - lzma_codec_free(&cdlz->base_decompressor); -#ifdef WANT_SUBCODE - zlib_codec_free(&cdlz->subcode_decompressor); -#endif - if (cdlz->buffer) - free(cdlz->buffer); -} - -chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) -{ -#ifdef WANT_RAW_DATA_SECTOR - uint8_t *sector; -#endif - uint32_t framenum; - cdlz_codec_data* cdlz = (cdlz_codec_data*)codec; - - /* determine header bytes */ - uint32_t frames = destlen / CD_FRAME_SIZE; - uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; - uint32_t ecc_bytes = (frames + 7) / 8; - uint32_t header_bytes = ecc_bytes + complen_bytes; - - /* extract compressed length of base */ - uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; - if (complen_bytes > 2) - complen_base = (complen_base << 8) | src[ecc_bytes + 2]; - - /* reset and decode */ - lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA); -#ifdef WANT_SUBCODE - if (header_bytes + complen_base >= complen) - return CHDERR_DECOMPRESSION_ERROR; - zlib_codec_decompress(&cdlz->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdlz->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); -#endif - - /* reassemble the data */ - for (framenum = 0; framenum < frames; framenum++) - { - memcpy(&dest[framenum * CD_FRAME_SIZE], &cdlz->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); -#ifdef WANT_SUBCODE - memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdlz->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); -#endif - -#ifdef WANT_RAW_DATA_SECTOR - /* reconstitute the ECC data and sync header */ - sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE]; - if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) - { - memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); - ecc_generate(sector); - } -#endif - } - return CHDERR_NONE; -} - -/* cdzl */ - -chd_error cdzl_codec_init(void *codec, uint32_t hunkbytes) -{ - chd_error ret; - cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; - - /* make sure the CHD's hunk size is an even multiple of the frame size */ - if (hunkbytes % CD_FRAME_SIZE != 0) - return CHDERR_CODEC_ERROR; - - cdzl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); - if (cdzl->buffer == NULL) - return CHDERR_OUT_OF_MEMORY; - - ret = zlib_codec_init(&cdzl->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); - if (ret != CHDERR_NONE) - return ret; - -#ifdef WANT_SUBCODE - ret = zlib_codec_init(&cdzl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); - if (ret != CHDERR_NONE) - return ret; -#endif - - return CHDERR_NONE; -} - -void cdzl_codec_free(void *codec) -{ - cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; - - zlib_codec_free(&cdzl->base_decompressor); -#ifdef WANT_SUBCODE - zlib_codec_free(&cdzl->subcode_decompressor); -#endif - if (cdzl->buffer) - free(cdzl->buffer); -} - -chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) -{ -#ifdef WANT_RAW_DATA_SECTOR - uint8_t *sector; -#endif - uint32_t framenum; - cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; - - /* determine header bytes */ - uint32_t frames = destlen / CD_FRAME_SIZE; - uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; - uint32_t ecc_bytes = (frames + 7) / 8; - uint32_t header_bytes = ecc_bytes + complen_bytes; - - /* extract compressed length of base */ - uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; - if (complen_bytes > 2) - complen_base = (complen_base << 8) | src[ecc_bytes + 2]; - - /* reset and decode */ - zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA); -#ifdef WANT_SUBCODE - zlib_codec_decompress(&cdzl->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdzl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); -#endif - - /* reassemble the data */ - for (framenum = 0; framenum < frames; framenum++) - { - memcpy(&dest[framenum * CD_FRAME_SIZE], &cdzl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); -#ifdef WANT_SUBCODE - memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdzl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); -#endif - -#ifdef WANT_RAW_DATA_SECTOR - /* reconstitute the ECC data and sync header */ - sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE]; - if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) - { - memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); - ecc_generate(sector); - } -#endif - } - return CHDERR_NONE; -} - /*************************************************************************** * CD FLAC DECOMPRESSOR *************************************************************************** @@ -855,6 +399,7 @@ static const codec_interface codec_interfaces[] = NULL }, +#ifdef HAVE_ZLIB /* standard zlib compression */ { CHDCOMPRESSION_ZLIB, @@ -887,7 +432,9 @@ static const codec_interface codec_interfaces[] = cdzl_codec_decompress, NULL }, +#endif +#ifdef HAVE_7ZIP /* V5 CD lzma compression */ { CHD_CODEC_CD_LZMA, @@ -898,6 +445,7 @@ static const codec_interface codec_interfaces[] = cdlz_codec_decompress, NULL }, +#endif /* V5 CD flac compression */ { @@ -2419,159 +1967,3 @@ static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metai /* if we get here, we didn't find it */ return CHDERR_METADATA_NOT_FOUND; } - -/*************************************************************************** - ZLIB COMPRESSION CODEC -***************************************************************************/ - -/*------------------------------------------------- - zlib_codec_init - initialize the ZLIB codec --------------------------------------------------*/ - -static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes) -{ - int zerr; - chd_error err; - zlib_codec_data *data = (zlib_codec_data*)codec; - - /* clear the buffers */ - memset(data, 0, sizeof(zlib_codec_data)); - - /* init the inflater first */ - data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */ - data->inflater.avail_in = 0; - data->inflater.zalloc = zlib_fast_alloc; - data->inflater.zfree = zlib_fast_free; - data->inflater.opaque = &data->allocator; - zerr = inflateInit2(&data->inflater, -MAX_WBITS); - - /* convert errors */ - if (zerr == Z_MEM_ERROR) - err = CHDERR_OUT_OF_MEMORY; - else if (zerr != Z_OK) - err = CHDERR_CODEC_ERROR; - else - err = CHDERR_NONE; - - return err; -} - -/*------------------------------------------------- - zlib_codec_free - free data for the ZLIB - codec --------------------------------------------------*/ - -static void zlib_codec_free(void *codec) -{ - zlib_codec_data *data = (zlib_codec_data *)codec; - - /* deinit the streams */ - if (data != NULL) - { - int i; - zlib_allocator alloc; - - inflateEnd(&data->inflater); - - /* free our fast memory */ - alloc = data->allocator; - for (i = 0; i < MAX_ZLIB_ALLOCS; i++) - if (alloc.allocptr[i]) - free(alloc.allocptr[i]); - } -} - -/*------------------------------------------------- - zlib_codec_decompress - decomrpess data using - the ZLIB codec --------------------------------------------------*/ - -static chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) -{ - zlib_codec_data *data = (zlib_codec_data *)codec; - int zerr; - - /* reset the decompressor */ - data->inflater.next_in = (Bytef *)src; - data->inflater.avail_in = complen; - data->inflater.total_in = 0; - data->inflater.next_out = (Bytef *)dest; - data->inflater.avail_out = destlen; - data->inflater.total_out = 0; - zerr = inflateReset(&data->inflater); - if (zerr != Z_OK) - return CHDERR_DECOMPRESSION_ERROR; - - /* do it */ - zerr = inflate(&data->inflater, Z_FINISH); - (void)zerr; - if (data->inflater.total_out != destlen) - return CHDERR_DECOMPRESSION_ERROR; - - return CHDERR_NONE; -} - -/*------------------------------------------------- - zlib_fast_alloc - fast malloc for ZLIB, which - allocates and frees memory frequently --------------------------------------------------*/ - -static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) -{ - zlib_allocator *alloc = (zlib_allocator *)opaque; - UINT32 *ptr; - int i; - - /* compute the size, rounding to the nearest 1k */ - size = (size * items + 0x3ff) & ~0x3ff; - - /* reuse a hunk if we can */ - for (i = 0; i < MAX_ZLIB_ALLOCS; i++) - { - ptr = alloc->allocptr[i]; - if (ptr && size == *ptr) - { - /* set the low bit of the size so we don't match next time */ - *ptr |= 1; - return ptr + 1; - } - } - - /* alloc a new one */ - ptr = (UINT32 *)malloc(size + sizeof(UINT32)); - if (!ptr) - return NULL; - - /* put it into the list */ - for (i = 0; i < MAX_ZLIB_ALLOCS; i++) - if (!alloc->allocptr[i]) - { - alloc->allocptr[i] = ptr; - break; - } - - /* set the low bit of the size so we don't match next time */ - *ptr = size | 1; - return ptr + 1; -} - -/*------------------------------------------------- - zlib_fast_free - fast free for ZLIB, which - allocates and frees memory frequently --------------------------------------------------*/ - -static void zlib_fast_free(voidpf opaque, voidpf address) -{ - zlib_allocator *alloc = (zlib_allocator *)opaque; - UINT32 *ptr = (UINT32 *)address - 1; - int i; - - /* find the hunk */ - for (i = 0; i < MAX_ZLIB_ALLOCS; i++) - if (ptr == alloc->allocptr[i]) - { - /* clear the low bit of the size to allow matches */ - *ptr &= ~1; - return; - } -} diff --git a/libretro-common/formats/libchdr/libchdr_lzma.c b/libretro-common/formats/libchdr/libchdr_lzma.c new file mode 100644 index 0000000000..284afcdd42 --- /dev/null +++ b/libretro-common/formats/libchdr/libchdr_lzma.c @@ -0,0 +1,356 @@ +/*************************************************************************** + + chd.c + + MAME Compressed Hunks of Data file format + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TRUE 1 +#define FALSE 0 + +/*************************************************************************** + * LZMA ALLOCATOR HELPER + *************************************************************************** + */ + +/*------------------------------------------------- + * lzma_fast_alloc - fast malloc for lzma, which + * allocates and frees memory frequently + *------------------------------------------------- + */ + +static void *lzma_fast_alloc(void *p, size_t size) +{ + int scan; + uint32_t *addr = NULL; + lzma_allocator *codec = (lzma_allocator *)(p); + + /* compute the size, rounding to the nearest 1k */ + size = (size + 0x3ff) & ~0x3ff; + + /* reuse a hunk if we can */ + for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + uint32_t *ptr = codec->allocptr[scan]; + if (ptr != NULL && size == *ptr) + { + /* set the low bit of the size so we don't match next time */ + *ptr |= 1; + return ptr + 1; + } + } + + /* alloc a new one and put it into the list */ + addr = (uint32_t *)malloc(sizeof(uint32_t) * (size + sizeof(uint32_t))); + if (!addr) + return NULL; + + for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + if (codec->allocptr[scan] == NULL) + { + codec->allocptr[scan] = addr; + break; + } + } + + /* set the low bit of the size so we don't match next time */ + *addr = size | 1; + return addr + 1; +} + +/*------------------------------------------------- + * lzma_fast_free - fast free for lzma, which + * allocates and frees memory frequently + *------------------------------------------------- + */ +static void lzma_fast_free(void *p, void *address) +{ + int scan; + uint32_t *ptr; + lzma_allocator *codec; + if (address == NULL) + return; + + codec = (lzma_allocator *)(p); + + /* find the hunk */ + ptr = (uint32_t *)(address) - 1; + for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + if (ptr == codec->allocptr[scan]) + { + /* clear the low bit of the size to allow matches */ + *ptr &= ~1; + return; + } + } +} + + +/*------------------------------------------------- + * lzma_allocator_init + *------------------------------------------------- + */ + +void lzma_allocator_init(void* p) +{ + lzma_allocator *codec = (lzma_allocator *)(p); + + /* reset pointer list */ + memset(codec->allocptr, 0, sizeof(codec->allocptr)); + codec->Alloc = lzma_fast_alloc; + codec->Free = lzma_fast_free; +} + +/*------------------------------------------------- + * lzma_allocator_free + *------------------------------------------------- + */ + +void lzma_allocator_free(void* p ) +{ + lzma_allocator *codec = (lzma_allocator *)(p); + + /* free our memory */ + int i; + for (i = 0 ; i < MAX_LZMA_ALLOCS ; i++) + { + if (codec->allocptr[i] != NULL) + free(codec->allocptr[i]); + } +} + + + +/*************************************************************************** + * LZMA DECOMPRESSOR + *************************************************************************** + */ + +/*------------------------------------------------- + * lzma_codec_init - constructor + *------------------------------------------------- + */ + +chd_error lzma_codec_init(void* codec, uint32_t hunkbytes) +{ + CLzmaEncProps encoder_props; + CLzmaEncHandle enc; + uint8_t decoder_props[LZMA_PROPS_SIZE]; + lzma_allocator* alloc; + size_t props_size; + lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; + + /* construct the decoder */ + LzmaDec_Construct(&lzma_codec->decoder); + + /* FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK + * This code assumes that the current version of the encoder imposes the same requirements on the + * decoder as the encoder used to produce the file. This is not necessarily true. The format + * needs to be changed so the encoder properties are written to the file. + + * configure the properties like the compressor did */ + LzmaEncProps_Init(&encoder_props); + encoder_props.level = 9; + encoder_props.reduceSize = hunkbytes; + LzmaEncProps_Normalize(&encoder_props); + + /* convert to decoder properties */ + alloc = &lzma_codec->allocator; + lzma_allocator_init(alloc); + enc = LzmaEnc_Create((ISzAlloc*)alloc); + if (!enc) + return CHDERR_DECOMPRESSION_ERROR; + if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK) + { + LzmaEnc_Destroy(enc, (ISzAlloc*)&alloc, (ISzAlloc*)&alloc); + return CHDERR_DECOMPRESSION_ERROR; + } + props_size = sizeof(decoder_props); + if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK) + { + LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc); + return CHDERR_DECOMPRESSION_ERROR; + } + LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc); + + /* do memory allocations */ + if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, (ISzAlloc*)alloc) != SZ_OK) + return CHDERR_DECOMPRESSION_ERROR; + + /* Okay */ + return CHDERR_NONE; +} + +/*------------------------------------------------- + * lzma_codec_free + *------------------------------------------------- + */ + +void lzma_codec_free(void* codec) +{ + lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; + lzma_allocator* alloc = &lzma_codec->allocator; + + /* free memory */ + lzma_allocator_free(alloc); + LzmaDec_Free(&lzma_codec->decoder, (ISzAlloc*)&lzma_codec->allocator); +} + +/*------------------------------------------------- + * decompress - decompress data using the LZMA + * codec + *------------------------------------------------- + */ + +chd_error lzma_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + ELzmaStatus status; + SRes res; + size_t consumedlen, decodedlen; + /* initialize */ + lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; + LzmaDec_Init(&lzma_codec->decoder); + + /* decode */ + consumedlen = complen; + decodedlen = destlen; + res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status); + if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen) + return CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; +} + +/* cdlz */ +chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes) +{ + chd_error ret; + cdlz_codec_data* cdlz = (cdlz_codec_data*) codec; + + /* allocate buffer */ + cdlz->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + if (cdlz->buffer == NULL) + return CHDERR_OUT_OF_MEMORY; + + ret = lzma_codec_init(&cdlz->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + if (ret != CHDERR_NONE) + return ret; + +#ifdef WANT_SUBCODE + ret = zlib_codec_init(&cdlz->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + if (ret != CHDERR_NONE) + return ret; +#endif + + return CHDERR_NONE; +} + +void cdlz_codec_free(void* codec) +{ + cdlz_codec_data* cdlz = (cdlz_codec_data*) codec; + + lzma_codec_free(&cdlz->base_decompressor); +#ifdef WANT_SUBCODE + zlib_codec_free(&cdlz->subcode_decompressor); +#endif + if (cdlz->buffer) + free(cdlz->buffer); +} + +chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ +#ifdef WANT_RAW_DATA_SECTOR + uint8_t *sector; +#endif + uint32_t framenum; + cdlz_codec_data* cdlz = (cdlz_codec_data*)codec; + + /* determine header bytes */ + uint32_t frames = destlen / CD_FRAME_SIZE; + uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; + uint32_t ecc_bytes = (frames + 7) / 8; + uint32_t header_bytes = ecc_bytes + complen_bytes; + + /* extract compressed length of base */ + uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; + if (complen_bytes > 2) + complen_base = (complen_base << 8) | src[ecc_bytes + 2]; + + /* reset and decode */ + lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA); +#ifdef WANT_SUBCODE + if (header_bytes + complen_base >= complen) + return CHDERR_DECOMPRESSION_ERROR; + zlib_codec_decompress(&cdlz->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdlz->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); +#endif + + /* reassemble the data */ + for (framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdlz->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); +#ifdef WANT_SUBCODE + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdlz->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); +#endif + +#ifdef WANT_RAW_DATA_SECTOR + /* reconstitute the ECC data and sync header */ + sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE]; + if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) + { + memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); + ecc_generate(sector); + } +#endif + } + return CHDERR_NONE; +} + diff --git a/libretro-common/formats/libchdr/libchdr_zlib.c b/libretro-common/formats/libchdr/libchdr_zlib.c new file mode 100644 index 0000000000..6446c33fa4 --- /dev/null +++ b/libretro-common/formats/libchdr/libchdr_zlib.c @@ -0,0 +1,300 @@ +/*************************************************************************** + + libchdr_zlib.c + + MAME Compressed Hunks of Data file format + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TRUE 1 +#define FALSE 0 + +/* cdzl */ + +chd_error cdzl_codec_init(void *codec, uint32_t hunkbytes) +{ + chd_error ret; + cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; + + /* make sure the CHD's hunk size is an even multiple of the frame size */ + if (hunkbytes % CD_FRAME_SIZE != 0) + return CHDERR_CODEC_ERROR; + + cdzl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + if (cdzl->buffer == NULL) + return CHDERR_OUT_OF_MEMORY; + + ret = zlib_codec_init(&cdzl->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + if (ret != CHDERR_NONE) + return ret; + +#ifdef WANT_SUBCODE + ret = zlib_codec_init(&cdzl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + if (ret != CHDERR_NONE) + return ret; +#endif + + return CHDERR_NONE; +} + +void cdzl_codec_free(void *codec) +{ + cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; + + zlib_codec_free(&cdzl->base_decompressor); +#ifdef WANT_SUBCODE + zlib_codec_free(&cdzl->subcode_decompressor); +#endif + if (cdzl->buffer) + free(cdzl->buffer); +} + +chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ +#ifdef WANT_RAW_DATA_SECTOR + uint8_t *sector; +#endif + uint32_t framenum; + cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; + + /* determine header bytes */ + uint32_t frames = destlen / CD_FRAME_SIZE; + uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; + uint32_t ecc_bytes = (frames + 7) / 8; + uint32_t header_bytes = ecc_bytes + complen_bytes; + + /* extract compressed length of base */ + uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; + if (complen_bytes > 2) + complen_base = (complen_base << 8) | src[ecc_bytes + 2]; + + /* reset and decode */ + zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA); +#ifdef WANT_SUBCODE + zlib_codec_decompress(&cdzl->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdzl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); +#endif + + /* reassemble the data */ + for (framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdzl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); +#ifdef WANT_SUBCODE + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdzl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); +#endif + +#ifdef WANT_RAW_DATA_SECTOR + /* reconstitute the ECC data and sync header */ + sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE]; + if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) + { + memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); + ecc_generate(sector); + } +#endif + } + return CHDERR_NONE; +} + +/*************************************************************************** + ZLIB COMPRESSION CODEC +***************************************************************************/ + +/*------------------------------------------------- + zlib_codec_init - initialize the ZLIB codec +-------------------------------------------------*/ + +chd_error zlib_codec_init(void *codec, uint32_t hunkbytes) +{ + int zerr; + chd_error err; + zlib_codec_data *data = (zlib_codec_data*)codec; + + /* clear the buffers */ + memset(data, 0, sizeof(zlib_codec_data)); + + /* init the inflater first */ + data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */ + data->inflater.avail_in = 0; + data->inflater.zalloc = zlib_fast_alloc; + data->inflater.zfree = zlib_fast_free; + data->inflater.opaque = &data->allocator; + zerr = inflateInit2(&data->inflater, -MAX_WBITS); + + /* convert errors */ + if (zerr == Z_MEM_ERROR) + err = CHDERR_OUT_OF_MEMORY; + else if (zerr != Z_OK) + err = CHDERR_CODEC_ERROR; + else + err = CHDERR_NONE; + + return err; +} + +/*------------------------------------------------- + zlib_codec_free - free data for the ZLIB + codec +-------------------------------------------------*/ + +void zlib_codec_free(void *codec) +{ + zlib_codec_data *data = (zlib_codec_data *)codec; + + /* deinit the streams */ + if (data != NULL) + { + int i; + zlib_allocator alloc; + + inflateEnd(&data->inflater); + + /* free our fast memory */ + alloc = data->allocator; + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (alloc.allocptr[i]) + free(alloc.allocptr[i]); + } +} + +/*------------------------------------------------- + zlib_codec_decompress - decomrpess data using + the ZLIB codec +-------------------------------------------------*/ + +chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + zlib_codec_data *data = (zlib_codec_data *)codec; + int zerr; + + /* reset the decompressor */ + data->inflater.next_in = (Bytef *)src; + data->inflater.avail_in = complen; + data->inflater.total_in = 0; + data->inflater.next_out = (Bytef *)dest; + data->inflater.avail_out = destlen; + data->inflater.total_out = 0; + zerr = inflateReset(&data->inflater); + if (zerr != Z_OK) + return CHDERR_DECOMPRESSION_ERROR; + + /* do it */ + zerr = inflate(&data->inflater, Z_FINISH); + (void)zerr; + if (data->inflater.total_out != destlen) + return CHDERR_DECOMPRESSION_ERROR; + + return CHDERR_NONE; +} + +/*------------------------------------------------- + zlib_fast_alloc - fast malloc for ZLIB, which + allocates and frees memory frequently +-------------------------------------------------*/ + +voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) +{ + zlib_allocator *alloc = (zlib_allocator *)opaque; + UINT32 *ptr; + int i; + + /* compute the size, rounding to the nearest 1k */ + size = (size * items + 0x3ff) & ~0x3ff; + + /* reuse a hunk if we can */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + { + ptr = alloc->allocptr[i]; + if (ptr && size == *ptr) + { + /* set the low bit of the size so we don't match next time */ + *ptr |= 1; + return ptr + 1; + } + } + + /* alloc a new one */ + ptr = (UINT32 *)malloc(size + sizeof(UINT32)); + if (!ptr) + return NULL; + + /* put it into the list */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (!alloc->allocptr[i]) + { + alloc->allocptr[i] = ptr; + break; + } + + /* set the low bit of the size so we don't match next time */ + *ptr = size | 1; + return ptr + 1; +} + +/*------------------------------------------------- + zlib_fast_free - fast free for ZLIB, which + allocates and frees memory frequently +-------------------------------------------------*/ + +void zlib_fast_free(voidpf opaque, voidpf address) +{ + zlib_allocator *alloc = (zlib_allocator *)opaque; + UINT32 *ptr = (UINT32 *)address - 1; + int i; + + /* find the hunk */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (ptr == alloc->allocptr[i]) + { + /* clear the low bit of the size to allow matches */ + *ptr &= ~1; + return; + } +} diff --git a/libretro-common/include/libchdr/chd.h b/libretro-common/include/libchdr/chd.h index 5180d8e3e9..9b4df95349 100644 --- a/libretro-common/include/libchdr/chd.h +++ b/libretro-common/include/libchdr/chd.h @@ -397,6 +397,8 @@ chd_error chd_codec_config(chd_file *chd, int param, void *config); /* return a string description of a codec */ const char *chd_get_codec_name(UINT32 codec); +extern const uint8_t s_cd_sync_header[12]; + #ifdef __cplusplus } #endif diff --git a/libretro-common/include/libchdr/libchdr_zlib.h b/libretro-common/include/libchdr/libchdr_zlib.h new file mode 100644 index 0000000000..e8cac0cc54 --- /dev/null +++ b/libretro-common/include/libchdr/libchdr_zlib.h @@ -0,0 +1,68 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles + *************************************************************************** + + libchr_zlib.h + + Zlib compression wrappers + +***************************************************************************/ + +#pragma once + +#ifndef __LIBCHDR_ZLIB_H__ +#define __LIBCHDR_ZLIB_H__ + +#include + +#include +#include "coretypes.h" + +#define MAX_ZLIB_ALLOCS 64 + +/* codec-private data for the ZLIB codec */ + +typedef struct _zlib_allocator zlib_allocator; +struct _zlib_allocator +{ + UINT32 * allocptr[MAX_ZLIB_ALLOCS]; +}; + +typedef struct _zlib_codec_data zlib_codec_data; +struct _zlib_codec_data +{ + z_stream inflater; + zlib_allocator allocator; +}; + + +/* codec-private data for the CDZL codec */ +typedef struct _cdzl_codec_data cdzl_codec_data; +struct _cdzl_codec_data { + /* internal state */ + zlib_codec_data base_decompressor; +#ifdef WANT_SUBCODE + zlib_codec_data subcode_decompressor; +#endif + uint8_t* buffer; +}; + +/* zlib compression codec */ +chd_error zlib_codec_init(void *codec, uint32_t hunkbytes); + +void zlib_codec_free(void *codec); + +chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size); + +void zlib_fast_free(voidpf opaque, voidpf address); + +/* cdzl compression codec */ +chd_error cdzl_codec_init(void* codec, uint32_t hunkbytes); + +void cdzl_codec_free(void* codec); + +chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +#endif /* __LIBCHDR_ZLIB_H__ */ diff --git a/libretro-common/include/libchdr/lzma.h b/libretro-common/include/libchdr/lzma.h new file mode 100644 index 0000000000..e3cccbc196 --- /dev/null +++ b/libretro-common/include/libchdr/lzma.h @@ -0,0 +1,72 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles + *************************************************************************** + + lzma.h + + LZMA compression wrappers + +***************************************************************************/ + +#pragma once + +#ifndef __LIBCHDR_LZMA_H__ +#define __LIBCHDR_LZMA_H__ + +#include + +#include +#include + +#include + +/* codec-private data for the LZMA codec */ +#define MAX_LZMA_ALLOCS 64 + +typedef struct _lzma_allocator lzma_allocator; +struct _lzma_allocator +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ + void (*FreeSz)(void *p, void *address, size_t size); /* address can be 0 */ + uint32_t* allocptr[MAX_LZMA_ALLOCS]; +}; + +typedef struct _lzma_codec_data lzma_codec_data; +struct _lzma_codec_data +{ + CLzmaDec decoder; + lzma_allocator allocator; +}; + +/* codec-private data for the CDLZ codec */ +typedef struct _cdlz_codec_data cdlz_codec_data; +struct _cdlz_codec_data { + /* internal state */ + lzma_codec_data base_decompressor; +#ifdef WANT_SUBCODE + zlib_codec_data subcode_decompressor; +#endif + uint8_t* buffer; +}; + +chd_error lzma_codec_init(void* codec, uint32_t hunkbytes); + +void lzma_codec_free(void* codec); + +/*------------------------------------------------- + * decompress - decompress data using the LZMA + * codec + *------------------------------------------------- + */ + +chd_error lzma_codec_decompress(void* codec, const uint8_t *src, + uint32_t complen, uint8_t *dest, uint32_t destlen); + +chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes); + +void cdlz_codec_free(void* codec); + +chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +#endif /* __LIBCHDR_LZMA_H__ */ From 997c24ae0c45a40e055f9c9d90debc7c13782350 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 22 Apr 2018 20:19:07 +0200 Subject: [PATCH 399/517] Make FLAC, zlib and LZMA support in libchdr optional --- Makefile.common | 31 ++- griffin/griffin.c | 13 ++ libretro-common/formats/libchdr/libchdr_chd.c | 203 +++++------------- .../formats/libchdr/libchdr_flac_codec.c | 163 ++++++++++++++ .../formats/libchdr/libchdr_lzma.c | 3 +- .../formats/libchdr/libchdr_zlib.c | 2 - libretro-common/include/libchdr/flac.h | 18 ++ .../include/libchdr/libchdr_zlib.h | 1 + qb/config.params.sh | 1 + 9 files changed, 265 insertions(+), 170 deletions(-) create mode 100644 libretro-common/formats/libchdr/libchdr_flac_codec.c diff --git a/Makefile.common b/Makefile.common index ef5f2b7a4d..81f850788e 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1495,32 +1495,27 @@ ifeq ($(HAVE_ZLIB), 1) endif endif -ifeq ($(HAVE_FLAC), 1) -ifeq ($(HAVE_7ZIP), 1) -ifeq ($(HAVE_ZLIB), 1) - HAVE_CHD = 1 - DEFINES += -DHAVE_CHD -DWANT_SUBCODE -DWANT_RAW_DATA_SECTOR +ifeq ($(HAVE_CHD), 1) CFLAGS += -I$(LIBRETRO_COMM_DIR)/formats/libchdr + DEFINES += -DHAVE_CHD -DWANT_SUBCODE -DWANT_RAW_DATA_SECTOR OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_bitstream.o \ $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_cdrom.o \ $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_chd.o \ - $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_flac.o \ $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_huffman.o \ $(LIBRETRO_COMM_DIR)/streams/chd_stream.o -endif -endif -endif + + ifeq ($(HAVE_FLAC), 1) + OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_flac.o \ + $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_flac_codec.o + endif -ifeq ($(HAVE_CHD), 1) -ifeq ($(HAVE_7ZIP), 1) - OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_lzma.o -endif -endif + ifeq ($(HAVE_7ZIP), 1) + OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_lzma.o + endif -ifeq ($(HAVE_CHD), 1) -ifeq ($(HAVE_ZLIB), 1) - OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_zlib.o -endif + ifeq ($(HAVE_ZLIB), 1) + OBJ += $(LIBRETRO_COMM_DIR)/formats/libchdr/libchdr_zlib.o + endif endif ifeq ($(HAVE_RTGA), 1) diff --git a/griffin/griffin.c b/griffin/griffin.c index e71cb8753f..6bab9b6631 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1310,7 +1310,20 @@ DEPENDENCIES #include "../libretro-common/formats/libchdr/libchdr_bitstream.c" #include "../libretro-common/formats/libchdr/libchdr_cdrom.c" #include "../libretro-common/formats/libchdr/libchdr_chd.c" + +#ifdef HAVE_FLAC #include "../libretro-common/formats/libchdr/libchdr_flac.c" +#include "../libretro-common/formats/libchdr/libchdr_flac_codec.c" +#endif + +#ifdef HAVE_ZLIB +#include "../libretro-common/formats/libchdr/libchdr_zlib.c" +#endif + +#ifdef HAVE_7ZIP +#include "../libretro-common/formats/libchdr/libchdr_lzma.c" +#endif + #include "../libretro-common/formats/libchdr/libchdr_huffman.c" #include "../libretro-common/streams/chd_stream.c" diff --git a/libretro-common/formats/libchdr/libchdr_chd.c b/libretro-common/formats/libchdr/libchdr_chd.c index 3712c926dd..d50091d0a4 100644 --- a/libretro-common/formats/libchdr/libchdr_chd.c +++ b/libretro-common/formats/libchdr/libchdr_chd.c @@ -45,11 +45,16 @@ #include #include #include -#include #include + +#ifdef HAVE_FLAC +#include +#endif + #ifdef HAVE_7ZIP #include #endif + #ifdef HAVE_ZLIB #include #endif @@ -185,18 +190,6 @@ struct _metadata_entry UINT8 flags; /* flag bits */ }; -/* codec-private data for the CDFL codec */ -typedef struct _cdfl_codec_data cdfl_codec_data; -struct _cdfl_codec_data { - /* internal state */ - int swap_endian; - flac_decoder decoder; -#ifdef WANT_SUBCODE - zlib_codec_data subcode_decompressor; -#endif - uint8_t* buffer; -}; - /* internal representation of an open CHD file */ struct _chd_file { @@ -228,7 +221,9 @@ struct _chd_file #ifdef HAVE_7ZIP cdlz_codec_data cdlz_codec_data; /* cdlz codec data */ #endif +#ifdef HAVE_FLAC cdfl_codec_data cdfl_codec_data; /* cdfl codec data */ +#endif #ifdef NEED_CACHE_HUNK UINT32 maxhunk; /* maximum hunk accessed */ @@ -263,118 +258,6 @@ static chd_error map_read(chd_file *chd); /* metadata management */ static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry); - -/* cdfl compression codec */ -static chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes); -static void cdfl_codec_free(void* codec); -static chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); - -/*************************************************************************** - * CD FLAC DECOMPRESSOR - *************************************************************************** - */ - -/*------------------------------------------------------ - * cdfl_codec_blocksize - return the optimal block size - *------------------------------------------------------ - */ - -static uint32_t cdfl_codec_blocksize(uint32_t bytes) -{ - /* determine FLAC block size, which must be 16-65535 - * clamp to 2k since that's supposed to be the sweet spot */ - uint32_t hunkbytes = bytes / 4; - while (hunkbytes > 2048) - hunkbytes /= 2; - return hunkbytes; -} - -chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes) -{ -#ifdef WANT_SUBCODE - chd_error ret; -#endif - uint16_t native_endian = 0; - cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; - - /* make sure the CHD's hunk size is an even multiple of the frame size */ - if (hunkbytes % CD_FRAME_SIZE != 0) - return CHDERR_CODEC_ERROR; - - cdfl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); - if (cdfl->buffer == NULL) - return CHDERR_OUT_OF_MEMORY; - - /* determine whether we want native or swapped samples */ - *(uint8_t *)(&native_endian) = 1; - cdfl->swap_endian = (native_endian & 1); - -#ifdef WANT_SUBCODE - /* init zlib inflater */ - ret = zlib_codec_init(&cdfl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); - if (ret != CHDERR_NONE) - return ret; -#endif - - /* flac decoder init */ - flac_decoder_init(&cdfl->decoder); - if (cdfl->decoder.decoder == NULL) - return CHDERR_OUT_OF_MEMORY; - - return CHDERR_NONE; -} - -void cdfl_codec_free(void *codec) -{ - cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; - flac_decoder_free(&cdfl->decoder); -#ifdef WANT_SUBCODE - zlib_codec_free(&cdfl->subcode_decompressor); -#endif - if (cdfl->buffer) - free(cdfl->buffer); -} - -chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) -{ - uint32_t framenum; - uint8_t *buffer; -#ifdef WANT_SUBCODE - uint32_t offset; - chd_error ret; -#endif - cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; - - /* reset and decode */ - uint32_t frames = destlen / CD_FRAME_SIZE; - - if (!flac_decoder_reset(&cdfl->decoder, 44100, 2, cdfl_codec_blocksize(frames * CD_MAX_SECTOR_DATA), src, complen)) - return CHDERR_DECOMPRESSION_ERROR; - buffer = &cdfl->buffer[0]; - if (!flac_decoder_decode_interleaved(&cdfl->decoder, (int16_t *)(buffer), frames * CD_MAX_SECTOR_DATA/4, cdfl->swap_endian)) - return CHDERR_DECOMPRESSION_ERROR; - -#ifdef WANT_SUBCODE - /* inflate the subcode data */ - offset = flac_decoder_finish(&cdfl->decoder); - ret = zlib_codec_decompress(&cdfl->subcode_decompressor, src + offset, complen - offset, &cdfl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); - if (ret != CHDERR_NONE) - return ret; -#else - flac_decoder_finish(&cdfl->decoder); -#endif - - /* reassemble the data */ - for (framenum = 0; framenum < frames; framenum++) - { - memcpy(&dest[framenum * CD_FRAME_SIZE], &cdfl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); -#ifdef WANT_SUBCODE - memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdfl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); -#endif - } - - return CHDERR_NONE; -} /*************************************************************************** CODEC INTERFACES ***************************************************************************/ @@ -447,6 +330,7 @@ static const codec_interface codec_interfaces[] = }, #endif +#ifdef HAVE_FLAC /* V5 CD flac compression */ { CHD_CODEC_CD_FLAC, @@ -457,6 +341,7 @@ static const codec_interface codec_interfaces[] = cdfl_codec_decompress, NULL }, +#endif }; /*************************************************************************** @@ -967,12 +852,14 @@ chd_error chd_open_file(RFILE *file, int mode, chd_file *parent, chd_file **chd) if (intfnum == ARRAY_SIZE(codec_interfaces)) EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT); +#ifdef HAVE_ZLIB /* initialize the codec */ if (newchd->codecintf[0]->init != NULL) - { - err = (*newchd->codecintf[0]->init)(&newchd->zlib_codec_data, newchd->header.hunkbytes); - (void)err; - } + { + err = (*newchd->codecintf[0]->init)(&newchd->zlib_codec_data, newchd->header.hunkbytes); + (void)err; + } +#endif } else { @@ -998,15 +885,21 @@ chd_error chd_open_file(RFILE *file, int mode, chd_file *parent, chd_file **chd) switch (newchd->header.compression[decompnum]) { case CHD_CODEC_CD_ZLIB: +#ifdef HAVE_ZLIB codec = &newchd->cdzl_codec_data; +#endif break; case CHD_CODEC_CD_LZMA: +#ifdef HAVE_7ZIP codec = &newchd->cdlz_codec_data; +#endif break; case CHD_CODEC_CD_FLAC: +#ifdef HAVE_FLAC codec = &newchd->cdfl_codec_data; +#endif break; } if (codec != NULL) @@ -1122,35 +1015,41 @@ void chd_close(chd_file *chd) /* deinit the codec */ if (chd->header.version < 5) { +#ifdef HAVE_ZLIB if (chd->codecintf[0] != NULL && chd->codecintf[0]->free != NULL) (*chd->codecintf[0]->free)(&chd->zlib_codec_data); +#endif } else { int i; /* Free the codecs */ for (i = 0 ; i < 4 ; i++) - { - void* codec = NULL; - switch (chd->codecintf[i]->compression) - { - case CHD_CODEC_CD_LZMA: - codec = &chd->cdlz_codec_data; - break; + { + void* codec = NULL; + switch (chd->codecintf[i]->compression) + { + case CHD_CODEC_CD_LZMA: +#ifdef HAVE_7ZIP + codec = &chd->cdlz_codec_data; +#endif + break; - case CHD_CODEC_CD_ZLIB: - codec = &chd->cdzl_codec_data; - break; + case CHD_CODEC_CD_ZLIB: +#ifdef HAVE_ZLIB + codec = &chd->cdzl_codec_data; +#endif + break; - case CHD_CODEC_CD_FLAC: - codec = &chd->cdfl_codec_data; - break; - } - if (codec) - { - (*chd->codecintf[i]->free)(codec); - } - } + case CHD_CODEC_CD_FLAC: +#ifdef HAVE_FLAC + codec = &chd->cdfl_codec_data; +#endif + break; + } + if (codec) + (*chd->codecintf[i]->free)(codec); + } /* Free the raw map */ if (chd->header.rawmap != NULL) @@ -1686,6 +1585,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des if (bytes == NULL) return CHDERR_READ_ERROR; +#ifdef HAVE_ZLIB /* now decompress using the codec */ err = CHDERR_NONE; codec = &chd->zlib_codec_data; @@ -1693,6 +1593,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des err = (*chd->codecintf[0]->decompress)(codec, chd->compressed, entry->length, dest, chd->header.hunkbytes); if (err != CHDERR_NONE) return err; +#endif } break; @@ -1774,15 +1675,21 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des switch (chd->codecintf[rawmap[0]]->compression) { case CHD_CODEC_CD_LZMA: +#ifdef HAVE_7ZIP codec = &chd->cdlz_codec_data; +#endif break; case CHD_CODEC_CD_ZLIB: +#ifdef HAVE_ZLIB codec = &chd->cdzl_codec_data; +#endif break; case CHD_CODEC_CD_FLAC: +#ifdef HAVE_FLAC codec = &chd->cdfl_codec_data; +#endif break; } if (codec==NULL) diff --git a/libretro-common/formats/libchdr/libchdr_flac_codec.c b/libretro-common/formats/libchdr/libchdr_flac_codec.c new file mode 100644 index 0000000000..83abadd3f2 --- /dev/null +++ b/libretro-common/formats/libchdr/libchdr_flac_codec.c @@ -0,0 +1,163 @@ +/*************************************************************************** + + libchdr_flac_codec.c + + MAME Compressed Hunks of Data file format + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TRUE 1 +#define FALSE 0 + +/*************************************************************************** + * CD FLAC DECOMPRESSOR + *************************************************************************** + */ + +/*------------------------------------------------------ + * cdfl_codec_blocksize - return the optimal block size + *------------------------------------------------------ + */ + +static uint32_t cdfl_codec_blocksize(uint32_t bytes) +{ + /* determine FLAC block size, which must be 16-65535 + * clamp to 2k since that's supposed to be the sweet spot */ + uint32_t hunkbytes = bytes / 4; + while (hunkbytes > 2048) + hunkbytes /= 2; + return hunkbytes; +} + +chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes) +{ +#ifdef WANT_SUBCODE + chd_error ret; +#endif + uint16_t native_endian = 0; + cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; + + /* make sure the CHD's hunk size is an even multiple of the frame size */ + if (hunkbytes % CD_FRAME_SIZE != 0) + return CHDERR_CODEC_ERROR; + + cdfl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + if (cdfl->buffer == NULL) + return CHDERR_OUT_OF_MEMORY; + + /* determine whether we want native or swapped samples */ + *(uint8_t *)(&native_endian) = 1; + cdfl->swap_endian = (native_endian & 1); + +#ifdef WANT_SUBCODE + /* init zlib inflater */ + ret = zlib_codec_init(&cdfl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + if (ret != CHDERR_NONE) + return ret; +#endif + + /* flac decoder init */ + flac_decoder_init(&cdfl->decoder); + if (cdfl->decoder.decoder == NULL) + return CHDERR_OUT_OF_MEMORY; + + return CHDERR_NONE; +} + +void cdfl_codec_free(void *codec) +{ + cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; + flac_decoder_free(&cdfl->decoder); +#ifdef WANT_SUBCODE + zlib_codec_free(&cdfl->subcode_decompressor); +#endif + if (cdfl->buffer) + free(cdfl->buffer); +} + +chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + uint32_t framenum; + uint8_t *buffer; +#ifdef WANT_SUBCODE + uint32_t offset; + chd_error ret; +#endif + cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; + + /* reset and decode */ + uint32_t frames = destlen / CD_FRAME_SIZE; + + if (!flac_decoder_reset(&cdfl->decoder, 44100, 2, cdfl_codec_blocksize(frames * CD_MAX_SECTOR_DATA), src, complen)) + return CHDERR_DECOMPRESSION_ERROR; + buffer = &cdfl->buffer[0]; + if (!flac_decoder_decode_interleaved(&cdfl->decoder, (int16_t *)(buffer), frames * CD_MAX_SECTOR_DATA/4, cdfl->swap_endian)) + return CHDERR_DECOMPRESSION_ERROR; + +#ifdef WANT_SUBCODE + /* inflate the subcode data */ + offset = flac_decoder_finish(&cdfl->decoder); + ret = zlib_codec_decompress(&cdfl->subcode_decompressor, src + offset, complen - offset, &cdfl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + if (ret != CHDERR_NONE) + return ret; +#else + flac_decoder_finish(&cdfl->decoder); +#endif + + /* reassemble the data */ + for (framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdfl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); +#ifdef WANT_SUBCODE + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdfl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); +#endif + } + + return CHDERR_NONE; +} diff --git a/libretro-common/formats/libchdr/libchdr_lzma.c b/libretro-common/formats/libchdr/libchdr_lzma.c index 284afcdd42..096c6efb0f 100644 --- a/libretro-common/formats/libchdr/libchdr_lzma.c +++ b/libretro-common/formats/libchdr/libchdr_lzma.c @@ -1,6 +1,6 @@ /*************************************************************************** - chd.c + libchdr_lzma_codec.c MAME Compressed Hunks of Data file format @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include diff --git a/libretro-common/formats/libchdr/libchdr_zlib.c b/libretro-common/formats/libchdr/libchdr_zlib.c index 6446c33fa4..44c642f81e 100644 --- a/libretro-common/formats/libchdr/libchdr_zlib.c +++ b/libretro-common/formats/libchdr/libchdr_zlib.c @@ -45,8 +45,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/libretro-common/include/libchdr/flac.h b/libretro-common/include/libchdr/flac.h index a9f84962af..2ce112d27a 100644 --- a/libretro-common/include/libchdr/flac.h +++ b/libretro-common/include/libchdr/flac.h @@ -14,6 +14,7 @@ #define __FLAC_H__ #include +#include "libchdr_zlib.h" #include "FLAC/ordinals.h" #include "FLAC/stream_decoder.h" @@ -49,4 +50,21 @@ int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t nu int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian); uint32_t flac_decoder_finish(flac_decoder* decoder); +/* codec-private data for the CDFL codec */ +typedef struct _cdfl_codec_data cdfl_codec_data; +struct _cdfl_codec_data { + /* internal state */ + int swap_endian; + flac_decoder decoder; +#ifdef WANT_SUBCODE + zlib_codec_data subcode_decompressor; +#endif + uint8_t* buffer; +}; + +/* cdfl compression codec */ +chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes); +void cdfl_codec_free(void* codec); +chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + #endif /* __FLAC_H__ */ diff --git a/libretro-common/include/libchdr/libchdr_zlib.h b/libretro-common/include/libchdr/libchdr_zlib.h index e8cac0cc54..4f3f141050 100644 --- a/libretro-common/include/libchdr/libchdr_zlib.h +++ b/libretro-common/include/libchdr/libchdr_zlib.h @@ -17,6 +17,7 @@ #include #include "coretypes.h" +#include "chd.h" #define MAX_ZLIB_ALLOCS 64 diff --git a/qb/config.params.sh b/qb/config.params.sh index 6b2aec7ffb..cc23750792 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -92,6 +92,7 @@ HAVE_NEON=no # ARM NEON optimizations HAVE_SSE=no # x86 SSE optimizations (SSE, SSE2) HAVE_FLOATHARD=no # Force hard float ABI (for ARM) HAVE_FLOATSOFTFP=no # Force soft float ABI (for ARM) +HAVE_CHD=yes # Compile in chd support HAVE_7ZIP=yes # Compile in 7z support HAVE_FLAC=auto # Compile in flac support HAVE_BUILTINFLAC=yes # Bake in flac support From ed742c48e0840b40a2ab65d3b8073ce1354879d2 Mon Sep 17 00:00:00 2001 From: gblues Date: Sun, 22 Apr 2018 17:34:20 -0700 Subject: [PATCH 400/517] Fix hotplugging == DETAILS So, it turns out that there *is* a autoconfig disconnect handler. Took digging through tasks/task_autodetect.c to find it! So, I added a call to the handler when the pad gets disconnected. This seems to solve the problem of the pad not disappearing from the menu. (At the very least, the user's pad index reverts to "none" which is still an improvement) == TESTING Tested manually, made sure it didn't crash or leak slots. --- input/common/hid/device_ds3.c | 3 +++ input/common/hid/device_wiiu_gca.c | 14 ++++++-------- input/common/hid/hid_device_driver.h | 1 + 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index bd4d6a1ad4..d21788e624 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -236,6 +236,9 @@ static void *ds3_pad_init(void *data, uint32_t slot, hid_driver_t *driver) static void ds3_pad_deinit(void *data) { ds3_instance_t *pad = (ds3_instance_t *)data; + if(pad) { + input_autoconfigure_disconnect(pad->slot, ds3_pad_connection.get_name(pad)); + } } static void ds3_get_buttons(void *data, input_bits_t *state) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index ab113494c4..2b8eecc046 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -193,12 +193,13 @@ static void *wiiu_gca_pad_init(void *data, uint32_t slot, hid_driver_t *driver) static void wiiu_gca_pad_deinit(void *data) { - gca_pad_t *pad = (gca_pad_t *)data; + gca_pad_t *pad = (gca_pad_t *)data; - if(pad) - { - free(pad); - } + if(pad) + { + input_autoconfigure_disconnect(pad->slot, wiiu_gca_pad_connection.get_name(pad)); + free(pad); + } } static void wiiu_gca_get_buttons(void *data, input_bits_t *state) @@ -278,7 +279,6 @@ static unsigned decode_axis(unsigned axis) { unsigned positive = AXIS_POS_GET(axis); unsigned negative = AXIS_NEG_GET(axis); - RARCH_LOG("[gca]: positive: %ud negative: %ud\n", positive, negative); return positive >= 4 ? negative : positive; } @@ -286,7 +286,6 @@ static unsigned decode_axis(unsigned axis) static int16_t wiiu_gca_get_axis(void *data, unsigned axis) { axis = decode_axis(axis); - RARCH_LOG("[gca]: decoded axis: %d\n", axis); int16_t val; gca_pad_t *pad = (gca_pad_t *)data; @@ -310,7 +309,6 @@ static int16_t wiiu_gca_get_axis(void *data, unsigned axis) if(val > 0x1000 || val < -0x1000) return 0; - RARCH_LOG("[gca]: reading axis %d: %04x\n", axis, val); return val; } diff --git a/input/common/hid/hid_device_driver.h b/input/common/hid/hid_device_driver.h index 18a8f45479..57cd9f6e40 100644 --- a/input/common/hid/hid_device_driver.h +++ b/input/common/hid/hid_device_driver.h @@ -21,6 +21,7 @@ #include "../../connect/joypad_connection.h" #include "../../include/hid_driver.h" #include "../../../verbosity.h" +#include "../../../tasks/tasks_internal.h" typedef struct hid_device { void *(*init)(void *handle); From f7135bcee6df2da937d065c50970eaf470bdbfaf Mon Sep 17 00:00:00 2001 From: gblues Date: Sun, 22 Apr 2018 23:47:07 -0700 Subject: [PATCH 401/517] Fix analog reading on GCA == DETAILS After a little trial and error, I got analog input working for the Wii U GC adapter. DS3 might work, but it's untested. --- input/common/hid/device_ds3.c | 8 ++-- input/common/hid/device_null.c | 8 +++- input/common/hid/device_wiiu_gca.c | 75 ++++++++++++++++++------------ 3 files changed, 58 insertions(+), 33 deletions(-) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index d21788e624..1c87ec6595 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -324,9 +324,11 @@ static int16_t ds3_get_axis(void *data, unsigned axis) if(!pad || axis >= 4) return 0; - val = (pad->data[6+axis] << 8) - 0x8000; -// val = (pad->data[7+axis] << 8) - 0x8000; - return (val > 0x1000 || val < -0x1000) ? 0 : val; + val = pad->data[6+axis]; + // val = pad->data[7+axis]; + val = (val - 128) * 256; + + return val; } static const char *ds3_get_name(void *data) diff --git a/input/common/hid/device_null.c b/input/common/hid/device_null.c index 783c615924..f5676c87b7 100644 --- a/input/common/hid/device_null.c +++ b/input/common/hid/device_null.c @@ -174,7 +174,13 @@ static void null_set_rumble(void *data, enum retro_rumble_effect effect, uint16_ } /** - * Read analog sticks. If the pad doesn't have any analog axis, just return 0 here. + * Read analog sticks. + * If the pad doesn't have any analog axis, just return 0 here. + * + * The return value must conform to the following characteristics: + * - (0, 0) is center + * - (-32768,-32768) is top-left + * - (32767,32767) is bottom-right */ static int16_t null_get_axis(void *data, unsigned axis) { diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 2b8eecc046..7e82dda288 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -15,6 +15,7 @@ */ #include #include "hid_device_driver.h" +#include "../../../wiiu/input/wiiu_hid.h" #ifdef WII static uint8_t activation_packet[] = { 0x01, 0x13 }; @@ -41,6 +42,7 @@ typedef struct gca_pad_data uint8_t data[9]; // pad data uint32_t slot; // slot this pad occupies uint32_t buttons; // digital button state + int16_t analog_state[3][2]; // analog state } gca_pad_t; @@ -244,6 +246,43 @@ static void update_buttons(gca_pad_t *pad) (1 << button_mapping[i]) : 0; } +#if 0 +const char *axes[] = { + "left x", + "left y", + "right x", + "right y" +}; +#endif + +static void update_analog_state(gca_pad_t *pad) +{ + int pad_axis; + int16_t interpolated; + int16_t stage1, stage2; + unsigned stick, axis; + uint8_t val; + + /* GameCube analog axis are 8-bit unsigned, where 128/128 is center. + * So, we subtract 128 to get a signed, 0-based value and then mulitply + * by 256 to get the 16-bit range RetroArch expects. */ + for(pad_axis = 0; pad_axis < 4; pad_axis++) + { + axis = pad_axis % 2 ? 0 : 1; + stick = pad_axis / 2; + interpolated = pad->data[3 + pad_axis]; + /* libretro requires "up" to be negative, so we invert the y axis */ + interpolated = (axis) ? + ((interpolated - 128) * 256) : + ((interpolated - 128) * -256); + + pad->analog_state[stick][axis] = interpolated; +#if 0 + RARCH_LOG("%s: %d\n", axes[pad_axis], interpolated); +#endif + } +} + /** * The USB packet provides a 9-byte data packet for each pad. @@ -264,6 +303,7 @@ static void wiiu_gca_packet_handler(void *data, uint8_t *packet, uint16_t size) memcpy(pad->data, packet, size); update_buttons(pad); + update_analog_state(pad); } @@ -275,41 +315,18 @@ static void wiiu_gca_set_rumble(void *data, enum retro_rumble_effect effect, uin (void)strength; } -static unsigned decode_axis(unsigned axis) -{ - unsigned positive = AXIS_POS_GET(axis); - unsigned negative = AXIS_NEG_GET(axis); - - return positive >= 4 ? negative : positive; -} - static int16_t wiiu_gca_get_axis(void *data, unsigned axis) { - axis = decode_axis(axis); - int16_t val; + axis_data axis_data; + gca_pad_t *pad = (gca_pad_t *)data; - if(!pad || axis >= 4) + pad_functions.read_axis_data(axis, &axis_data); + + if(!pad || axis_data.axis >= 4) return 0; - val = pad->data[3+axis]; - - switch(axis) - { - /* The Y axes are inverted. */ - case 0: /* left Y */ - case 2: /* right Y */ - val = 0x8000 - (val << 8); - break; - default: - val = (val << 8) - 0x8000; - break; - } - - if(val > 0x1000 || val < -0x1000) - return 0; - - return val; + return pad_functions.get_axis_value(axis_data.axis, pad->analog_state, axis_data.is_negative); } static const char *wiiu_gca_get_name(void *data) From b41c4ed6248762831e55d4a62759ac4080f6a864 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Mon, 23 Apr 2018 10:22:54 +0200 Subject: [PATCH 402/517] Update --- libretro-common/formats/libchdr/libchdr_chd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libretro-common/formats/libchdr/libchdr_chd.c b/libretro-common/formats/libchdr/libchdr_chd.c index d50091d0a4..79a50aac2f 100644 --- a/libretro-common/formats/libchdr/libchdr_chd.c +++ b/libretro-common/formats/libchdr/libchdr_chd.c @@ -1569,7 +1569,6 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des if (chd->header.version < 5) { - void* codec; map_entry *entry = &chd->map[hunknum]; UINT32 bytes; From c18d2e0432ada7dcc427c8027009f46ef0af3cfe Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 10:49:15 +0200 Subject: [PATCH 403/517] Silence warnings --- menu/widgets/menu_osk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu/widgets/menu_osk.c b/menu/widgets/menu_osk.c index 1e2342bb6f..5bc3c6aa5f 100644 --- a/menu/widgets/menu_osk.c +++ b/menu/widgets/menu_osk.c @@ -32,7 +32,7 @@ #include "../../input/input_driver.h" -static const char *osk_grid[45] = {NULL}; +static char *osk_grid[45] = {NULL}; static int osk_ptr = 0; static enum osk_type osk_idx = OSK_LOWERCASE_LATIN; From eb69025ca88dfe5e8c5d90f8b82dc3de545000a0 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 11:30:38 +0200 Subject: [PATCH 404/517] Cleanups --- gfx/drivers/gl.c | 9 +++------ gfx/drivers_renderchain/gl1_renderchain.c | 2 -- menu/drivers/materialui.c | 5 +++-- menu/widgets/menu_osk.c | 2 +- menu/widgets/menu_osk.h | 2 +- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index ffb6073600..710866283e 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -1687,13 +1687,9 @@ static void gl_begin_debug(gl_t *gl) extern gl_renderchain_driver_t gl2_renderchain; static const gl_renderchain_driver_t *renderchain_gl_drivers[] = { -#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) - &gl2_renderchain, -#endif - NULL + &gl2_renderchain }; - static bool renderchain_gl_init_first( const gl_renderchain_driver_t **renderchain_driver, void **renderchain_handle) @@ -1715,7 +1711,8 @@ static bool renderchain_gl_init_first( return false; } -static void *gl_init(const video_info_t *video, const input_driver_t **input, void **input_data) +static void *gl_init(const video_info_t *video, + const input_driver_t **input, void **input_data) { gfx_ctx_mode_t mode; gfx_ctx_input_t inp; diff --git a/gfx/drivers_renderchain/gl1_renderchain.c b/gfx/drivers_renderchain/gl1_renderchain.c index 3eb1f887c7..f4f06acb43 100644 --- a/gfx/drivers_renderchain/gl1_renderchain.c +++ b/gfx/drivers_renderchain/gl1_renderchain.c @@ -55,8 +55,6 @@ typedef struct gl1_renderchain void *empty; } gl1_renderchain_t; -GLenum min_filter_to_mag(GLenum type); - void gl1_renderchain_free(void *data, void *chain_data) { (void)chain_data; diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 60e58e04e6..f6d6144aee 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -408,9 +408,10 @@ static void materialui_draw_tab( } /* Draw the onscreen keyboard */ -static void materialui_render_keyboard(materialui_handle_t *mui, +static void materialui_render_keyboard( + materialui_handle_t *mui, video_frame_info_t *video_info, - const char *grid[], unsigned id) + char *grid[], unsigned id) { int ptr_width, ptr_height; unsigned i; diff --git a/menu/widgets/menu_osk.c b/menu/widgets/menu_osk.c index 5bc3c6aa5f..65e17b04cc 100644 --- a/menu/widgets/menu_osk.c +++ b/menu/widgets/menu_osk.c @@ -148,7 +148,7 @@ void menu_event_osk_iterate(void) } } -const char** menu_event_get_osk_grid(void) +char** menu_event_get_osk_grid(void) { return osk_grid; } diff --git a/menu/widgets/menu_osk.h b/menu/widgets/menu_osk.h index be11d86e7f..2c460a7efb 100644 --- a/menu/widgets/menu_osk.h +++ b/menu/widgets/menu_osk.h @@ -59,7 +59,7 @@ void menu_event_osk_append(int a); void menu_event_osk_iterate(void); -const char** menu_event_get_osk_grid(void); +char** menu_event_get_osk_grid(void); RETRO_END_DECLS From 54ec4e47e82b9ab6534337de9262790f19bad002 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 11:31:39 +0200 Subject: [PATCH 405/517] Silence another warning --- menu/drivers/xmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 81d3432424..b40314a9d9 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -802,7 +802,7 @@ static void xmb_messagebox(void *data, const char *message) static void xmb_render_keyboard(xmb_handle_t *xmb, video_frame_info_t *video_info, - const char *grid[], unsigned id) + char *grid[], unsigned id) { unsigned i; int ptr_width, ptr_height; From f8ccf50fd475e05a96d1965692189e9217fd57a1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 11:36:15 +0200 Subject: [PATCH 406/517] Make materialui_render_keyboard and xmb_render_keyboard identical --- menu/drivers/materialui.c | 27 ++++++++++++++++----------- menu/drivers/xmb.c | 5 +++-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index f6d6144aee..abbbb6d218 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -438,7 +438,7 @@ static void materialui_render_keyboard( width, height, &dark[0]); - ptr_width = width / 11; + ptr_width = width / 11; ptr_height = height / 10; if (ptr_width >= ptr_height) @@ -450,21 +450,26 @@ static void materialui_render_keyboard( uintptr_t texture = mui->textures.list[MUI_TEXTURE_KEY]; if (i == id) + { texture = mui->textures.list[MUI_TEXTURE_KEY_HOVER]; - menu_display_blend_begin(video_info); + menu_display_blend_begin(video_info); - menu_display_draw_texture( - video_info, - width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width, - height/2.0 + ptr_height*1.5 + line_y, - ptr_width, ptr_height, - width, height, - &white[0], - texture); + menu_display_draw_texture( + video_info, + width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width, + height/2.0 + ptr_height*1.5 + line_y, + ptr_width, ptr_height, + width, height, + &white[0], + texture); + + menu_display_blend_end(video_info); + } menu_display_draw_text(mui->font, grid[i], - width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width + ptr_width/2.0, + width/2.0 - (11*ptr_width)/2.0 + (i % 11) + * ptr_width + ptr_width/2.0, height/2.0 + ptr_height + line_y + mui->font->size / 3, width, height, 0xffffffff, TEXT_ALIGN_CENTER, 1.0f, false, 0); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index b40314a9d9..6aa26e5c81 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -828,7 +828,7 @@ static void xmb_render_keyboard(xmb_handle_t *xmb, width, height, &dark[0]); - ptr_width = width / 11; + ptr_width = width / 11; ptr_height = height / 10; if (ptr_width >= ptr_height) @@ -857,7 +857,8 @@ static void xmb_render_keyboard(xmb_handle_t *xmb, } menu_display_draw_text(xmb->font, grid[i], - width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width + ptr_width/2.0, + width/2.0 - (11*ptr_width)/2.0 + (i % 11) + * ptr_width + ptr_width/2.0, height/2.0 + ptr_height + line_y + xmb->font->size / 3, width, height, 0xffffffff, TEXT_ALIGN_CENTER, 1.0f, false, 0); From 8bc3283bd5762b6b652a01a9fa572c258402bd97 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 11:42:55 +0200 Subject: [PATCH 407/517] Create menu_display_draw_keyboard --- menu/drivers/materialui.c | 73 ++------------------------------------- menu/drivers/xmb.c | 69 ++---------------------------------- menu/menu_driver.c | 65 ++++++++++++++++++++++++++++++++++ menu/menu_driver.h | 5 +++ 4 files changed, 76 insertions(+), 136 deletions(-) diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index abbbb6d218..a3eae3f18c 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -407,75 +407,6 @@ static void materialui_draw_tab( &tab_color[0]); } -/* Draw the onscreen keyboard */ -static void materialui_render_keyboard( - materialui_handle_t *mui, - video_frame_info_t *video_info, - char *grid[], unsigned id) -{ - int ptr_width, ptr_height; - unsigned i; - unsigned width = video_info->width; - unsigned height = video_info->height; - float dark[16] = { - 0.00, 0.00, 0.00, 0.85, - 0.00, 0.00, 0.00, 0.85, - 0.00, 0.00, 0.00, 0.85, - 0.00, 0.00, 0.00, 0.85, - }; - - float white[16] = { - 1.00, 1.00, 1.00, 1.00, - 1.00, 1.00, 1.00, 1.00, - 1.00, 1.00, 1.00, 1.00, - 1.00, 1.00, 1.00, 1.00, - }; - - menu_display_draw_quad( - video_info, - 0, - height/2.0, width, height/2.0, - width, height, - &dark[0]); - - ptr_width = width / 11; - ptr_height = height / 10; - - if (ptr_width >= ptr_height) - ptr_width = ptr_height; - - for (i = 0; i < 44; i++) - { - int line_y = (i / 11)*height/10.0; - uintptr_t texture = mui->textures.list[MUI_TEXTURE_KEY]; - - if (i == id) - { - texture = mui->textures.list[MUI_TEXTURE_KEY_HOVER]; - - menu_display_blend_begin(video_info); - - menu_display_draw_texture( - video_info, - width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width, - height/2.0 + ptr_height*1.5 + line_y, - ptr_width, ptr_height, - width, height, - &white[0], - texture); - - menu_display_blend_end(video_info); - } - - menu_display_draw_text(mui->font, grid[i], - width/2.0 - (11*ptr_width)/2.0 + (i % 11) - * ptr_width + ptr_width/2.0, - height/2.0 + ptr_height + line_y + mui->font->size / 3, - width, height, 0xffffffff, TEXT_ALIGN_CENTER, 1.0f, - false, 0); - } -} - /* Returns the OSK key at a given position */ static int materialui_osk_ptr_at_pos(void *data, int x, int y, unsigned width, unsigned height) @@ -667,7 +598,9 @@ static void materialui_render_messagebox(materialui_handle_t *mui, } if (menu_input_dialog_get_display_kb()) - materialui_render_keyboard(mui, + menu_display_draw_keyboard( + mui->textures.list[MUI_TEXTURE_KEY_HOVER], + mui->font, video_info, menu_event_get_osk_grid(), menu_event_get_osk_ptr()); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 6aa26e5c81..ea6c3002a3 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -800,71 +800,6 @@ static void xmb_messagebox(void *data, const char *message) xmb->box_message = strdup(message); } -static void xmb_render_keyboard(xmb_handle_t *xmb, - video_frame_info_t *video_info, - char *grid[], unsigned id) -{ - unsigned i; - int ptr_width, ptr_height; - unsigned width = video_info->width; - unsigned height = video_info->height; - float dark[16] = { - 0.00, 0.00, 0.00, 0.85, - 0.00, 0.00, 0.00, 0.85, - 0.00, 0.00, 0.00, 0.85, - 0.00, 0.00, 0.00, 0.85, - }; - - float white[16]= { - 1.00, 1.00, 1.00, 1.00, - 1.00, 1.00, 1.00, 1.00, - 1.00, 1.00, 1.00, 1.00, - 1.00, 1.00, 1.00, 1.00, - }; - - menu_display_draw_quad( - video_info, - 0, height/2.0, width, height/2.0, - width, height, - &dark[0]); - - ptr_width = width / 11; - ptr_height = height / 10; - - if (ptr_width >= ptr_height) - ptr_width = ptr_height; - - for (i = 0; i < 44; i++) - { - int line_y = (i / 11) * height / 10.0; - - if (i == id) - { - uintptr_t texture = xmb->textures.list[XMB_TEXTURE_KEY_HOVER]; - - menu_display_blend_begin(video_info); - - menu_display_draw_texture( - video_info, - width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width, - height/2.0 + ptr_height*1.5 + line_y, - ptr_width, ptr_height, - width, height, - &white[0], - texture); - - menu_display_blend_end(video_info); - } - - menu_display_draw_text(xmb->font, grid[i], - width/2.0 - (11*ptr_width)/2.0 + (i % 11) - * ptr_width + ptr_width/2.0, - height/2.0 + ptr_height + line_y + xmb->font->size / 3, - width, height, 0xffffffff, TEXT_ALIGN_CENTER, 1.0f, - false, 0); - } -} - /* Returns the OSK key at a given position */ static int xmb_osk_ptr_at_pos(void *data, int x, int y, unsigned width, unsigned height) { @@ -966,7 +901,9 @@ static void xmb_render_messagebox_internal( } if (menu_input_dialog_get_display_kb()) - xmb_render_keyboard(xmb, + menu_display_draw_keyboard( + xmb->textures.list[XMB_TEXTURE_KEY_HOVER], + xmb->font, video_info, menu_event_get_osk_grid(), menu_event_get_osk_ptr()); diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 2169895519..f841897635 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -1331,6 +1331,71 @@ void menu_display_snow(int width, int height) } } +void menu_display_draw_keyboard( + uintptr_t hover_texture, + const font_data_t *font, + video_frame_info_t *video_info, + char *grid[], unsigned id) +{ + unsigned i; + int ptr_width, ptr_height; + unsigned width = video_info->width; + unsigned height = video_info->height; + float dark[16] = { + 0.00, 0.00, 0.00, 0.85, + 0.00, 0.00, 0.00, 0.85, + 0.00, 0.00, 0.00, 0.85, + 0.00, 0.00, 0.00, 0.85, + }; + + float white[16]= { + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + }; + + menu_display_draw_quad( + video_info, + 0, height/2.0, width, height/2.0, + width, height, + &dark[0]); + + ptr_width = width / 11; + ptr_height = height / 10; + + if (ptr_width >= ptr_height) + ptr_width = ptr_height; + + for (i = 0; i < 44; i++) + { + int line_y = (i / 11) * height / 10.0; + + if (i == id) + { + menu_display_blend_begin(video_info); + + menu_display_draw_texture( + video_info, + width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width, + height/2.0 + ptr_height*1.5 + line_y, + ptr_width, ptr_height, + width, height, + &white[0], + hover_texture); + + menu_display_blend_end(video_info); + } + + menu_display_draw_text(font, grid[i], + width/2.0 - (11*ptr_width)/2.0 + (i % 11) + * ptr_width + ptr_width/2.0, + height/2.0 + ptr_height + line_y + font->size / 3, + width, height, 0xffffffff, TEXT_ALIGN_CENTER, 1.0f, + false, 0); + } +} + /* Draw text on top of the screen. */ void menu_display_draw_text( diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 6cf794de48..10f0a88e46 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -675,6 +675,11 @@ void menu_display_clear_color(menu_display_ctx_clearcolor_t *color, video_frame_info_t *video_info); void menu_display_draw(menu_display_ctx_draw_t *draw, video_frame_info_t *video_info); +void menu_display_draw_keyboard( + uintptr_t hover_texture, + const font_data_t *font, + video_frame_info_t *video_info, + char *grid[], unsigned id); void menu_display_draw_pipeline(menu_display_ctx_draw_t *draw, video_frame_info_t *video_info); From bac0d22ce95129905f6a9a9ed894dcb90ccc1428 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 11:58:18 +0200 Subject: [PATCH 408/517] Create menu_display_osk_ptr_at_pos --- menu/drivers/materialui.c | 33 +-------------------------------- menu/drivers/xmb.c | 32 +------------------------------- menu/menu_driver.c | 25 +++++++++++++++++++++++++ menu/menu_driver.h | 15 ++++++++++++--- 4 files changed, 39 insertions(+), 66 deletions(-) diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index a3eae3f18c..c179a8fab1 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -407,37 +407,6 @@ static void materialui_draw_tab( &tab_color[0]); } -/* Returns the OSK key at a given position */ -static int materialui_osk_ptr_at_pos(void *data, int x, int y, - unsigned width, unsigned height) -{ - unsigned i; - int ptr_width, ptr_height; - materialui_handle_t *mui = (materialui_handle_t*)data; - - if (!mui) - return -1; - - ptr_width = width / 11; - ptr_height = height / 10; - - if (ptr_width >= ptr_height) - ptr_width = ptr_height; - - for (i = 0; i < 44; i++) - { - int line_y = (i / 11)*height/10.0; - int ptr_x = width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width; - int ptr_y = height/2.0 + ptr_height*1.5 + line_y - ptr_height; - - if (x > ptr_x && x < ptr_x + ptr_width - && y > ptr_y && y < ptr_y + ptr_height) - return i; - } - - return -1; -} - /* Draw the tabs background */ static void materialui_draw_tab_begin( materialui_handle_t *mui, @@ -2813,7 +2782,7 @@ menu_ctx_driver_t menu_ctx_mui = { NULL, NULL, NULL, - materialui_osk_ptr_at_pos, + menu_display_osk_ptr_at_pos, NULL, NULL, materialui_pointer_down, diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index ea6c3002a3..3926bf1a1c 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -800,36 +800,6 @@ static void xmb_messagebox(void *data, const char *message) xmb->box_message = strdup(message); } -/* Returns the OSK key at a given position */ -static int xmb_osk_ptr_at_pos(void *data, int x, int y, unsigned width, unsigned height) -{ - unsigned i; - int ptr_width, ptr_height; - xmb_handle_t *xmb = (xmb_handle_t*)data; - - if (!xmb) - return -1; - - ptr_width = width / 11; - ptr_height = height / 10; - - if (ptr_width >= ptr_height) - ptr_width = ptr_height; - - for (i = 0; i < 44; i++) - { - int line_y = (i / 11)*height/10.0; - int ptr_x = width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width; - int ptr_y = height/2.0 + ptr_height*1.5 + line_y - ptr_height; - - if (x > ptr_x && x < ptr_x + ptr_width - && y > ptr_y && y < ptr_y + ptr_height) - return i; - } - - return -1; -} - static void xmb_render_messagebox_internal( video_frame_info_t *video_info, xmb_handle_t *xmb, const char *message, float* coord_white) @@ -5139,7 +5109,7 @@ menu_ctx_driver_t menu_ctx_xmb = { xmb_update_thumbnail_image, xmb_set_thumbnail_system, xmb_set_thumbnail_content, - xmb_osk_ptr_at_pos, + menu_display_osk_ptr_at_pos, xmb_update_savestate_thumbnail_path, xmb_update_savestate_thumbnail_image }; diff --git a/menu/menu_driver.c b/menu/menu_driver.c index f841897635..45580c12f5 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -196,6 +196,31 @@ static unsigned scroll_index_size = 0; static unsigned scroll_acceleration = 0; static size_t menu_driver_selection_ptr = 0; +/* Returns the OSK key at a given position */ +int menu_display_osk_ptr_at_pos(void *data, int x, int y, + unsigned width, unsigned height) +{ + unsigned i; + int ptr_width = width / 11; + int ptr_height = height / 10; + + if (ptr_width >= ptr_height) + ptr_width = ptr_height; + + for (i = 0; i < 44; i++) + { + int line_y = (i / 11)*height/10.0; + int ptr_x = width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width; + int ptr_y = height/2.0 + ptr_height*1.5 + line_y - ptr_height; + + if (x > ptr_x && x < ptr_x + ptr_width + && y > ptr_y && y < ptr_y + ptr_height) + return i; + } + + return -1; +} + enum menu_toggle_reason menu_display_toggle_get_reason(void) { return menu_display_toggle_reason; diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 10f0a88e46..e27825514b 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -745,11 +745,20 @@ void menu_display_draw_text( #define menu_display_set_alpha(color, alpha_value) (color[3] = color[7] = color[11] = color[15] = (alpha_value)) -font_data_t *menu_display_font(enum application_special_type type, float font_size, +font_data_t *menu_display_font( + enum application_special_type type, + float font_size, bool video_is_threaded); -void menu_display_reset_textures_list(const char *texture_path, const char *iconpath, - uintptr_t *item, enum texture_filter_type filter_type); +void menu_display_reset_textures_list( + const char *texture_path, + const char *iconpath, + uintptr_t *item, + enum texture_filter_type filter_type); + +/* Returns the OSK key at a given position */ +int menu_display_osk_ptr_at_pos(void *data, int x, int y, + unsigned width, unsigned height); void menu_driver_destroy(void); From 5370c59201f5fbda2c1e7dde9ba7b6402d8e8235 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 12:03:50 +0200 Subject: [PATCH 409/517] Cleanups/style nits --- menu/drivers/materialui.c | 100 ++++++------ menu/drivers/xmb.c | 312 +++++++++++++++++++------------------- 2 files changed, 208 insertions(+), 204 deletions(-) diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index c179a8fab1..26e1e1f9df 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -605,7 +605,7 @@ static void materialui_compute_entries_box(materialui_handle_t* mui, int width) char *sublabel_str = NULL; unsigned lines = 0; materialui_node_t *node = (materialui_node_t*) - file_list_get_userdata_at_offset(list, i); + file_list_get_userdata_at_offset(list, i); menu_entry_init(&entry); menu_entry_get(&entry, 0, i, NULL, true); @@ -641,7 +641,7 @@ static void materialui_compute_entries_box(materialui_handle_t* mui, int width) } /* Called on each frame. We use this callback to implement the touch scroll -with acceleration */ + with acceleration */ static void materialui_render(void *data, bool is_idle) { menu_animation_ctx_delta_t delta; @@ -686,12 +686,12 @@ static void materialui_render(void *data, bool is_idle) for (ii = 0; ii < entries_end; ii++) { materialui_node_t *node = (materialui_node_t*) - file_list_get_userdata_at_offset(list, ii); + file_list_get_userdata_at_offset(list, ii); if (pointer_y > (-mui->scroll_y + header_height + node->y) - && pointer_y < (-mui->scroll_y + header_height + node->y + node->line_height) - ) - menu_input_ctl(MENU_INPUT_CTL_POINTER_PTR, &ii); + && pointer_y < (-mui->scroll_y + header_height + node->y + node->line_height) + ) + menu_input_ctl(MENU_INPUT_CTL_POINTER_PTR, &ii); } menu_input_ctl(MENU_INPUT_CTL_POINTER_ACCEL_READ, &old_accel_val); @@ -712,12 +712,12 @@ static void materialui_render(void *data, bool is_idle) for (ii = 0; ii < entries_end; ii++) { materialui_node_t *node = (materialui_node_t*) - file_list_get_userdata_at_offset(list, ii); + file_list_get_userdata_at_offset(list, ii); if (mouse_y > (-mui->scroll_y + header_height + node->y) - && mouse_y < (-mui->scroll_y + header_height + node->y + node->line_height) - ) - menu_input_ctl(MENU_INPUT_CTL_MOUSE_PTR, &ii); + && mouse_y < (-mui->scroll_y + header_height + node->y + node->line_height) + ) + menu_input_ctl(MENU_INPUT_CTL_MOUSE_PTR, &ii); } } @@ -799,7 +799,7 @@ static void materialui_render_label_value( do_draw_text = true; } else if (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) || - (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)))) + (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)))) { if (mui->textures.list[MUI_TEXTURE_SWITCH_ON]) { @@ -896,7 +896,7 @@ static void materialui_render_label_value( 0, 1, &label_color[0] - ); + ); if (texture_switch) { @@ -959,7 +959,7 @@ static void materialui_render_menu_list( char *rich_label = NULL; bool entry_selected = false; materialui_node_t *node = (materialui_node_t*) - file_list_get_userdata_at_offset(list, i); + file_list_get_userdata_at_offset(list, i); size_t selection = menu_navigation_get_selection(); int y = header_height - mui->scroll_y + sum; @@ -982,21 +982,21 @@ static void materialui_render_menu_list( /* Render label, value, and associated icons */ materialui_render_label_value( - mui, - video_info, - node, - (int)i, - y, - width, - height, - frame_count / 20, - font_hover_color, - entry_selected, - rich_label, - entry_value, - menu_list_color, - sublabel_color - ); + mui, + video_info, + node, + (int)i, + y, + width, + height, + frame_count / 20, + font_hover_color, + entry_selected, + rich_label, + entry_value, + menu_list_color, + sublabel_color + ); menu_entry_free(&entry); free(rich_label); @@ -1075,7 +1075,7 @@ static void materialui_draw_bg(menu_display_ctx_draw_t *draw, } /* Main function of the menu driver. Takes care of drawing the header, the tabs, -and the menu list */ + and the menu list */ static void materialui_frame(void *data, video_frame_info_t *video_info) { /* This controls the main background color */ @@ -1236,7 +1236,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) clearcolor.a = 0.75f; break; case MATERIALUI_THEME_GREEN: - hex32_to_rgba_normalized(0x4CAF50, green_500, 1.00); + hex32_to_rgba_normalized(0x4CAF50, green_500, 1.00); hex32_to_rgba_normalized(0x4CAF50, header_bg_color_real, 1.00); hex32_to_rgba_normalized(0xC8E6C9, green_50, 0.90); hex32_to_rgba_normalized(0xFFFFFF, footer_bg_color_real, 1.00); @@ -1575,9 +1575,9 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) if (mui->font) menu_display_draw_text(mui->font, title_buf, - title_margin, - header_height / 2 + mui->font->size / 3, - width, height, font_header_color, TEXT_ALIGN_LEFT, 1.0f, false, 0); + title_margin, + header_height / 2 + mui->font->size / 3, + width, height, font_header_color, TEXT_ALIGN_LEFT, 1.0f, false, 0); materialui_draw_scrollbar(mui, video_info, width, height, &grey_bg[0]); @@ -1591,7 +1591,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) snprintf(msg, sizeof(msg), "%s\n%s", label, str); materialui_render_messagebox(mui, video_info, - msg, &body_bg_color[0], font_hover_color); + msg, &body_bg_color[0], font_hover_color); } if (!string_is_empty(mui->box_message)) @@ -1600,7 +1600,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) 0, 0, width, height, width, height, &black_bg[0]); materialui_render_messagebox(mui, video_info, - mui->box_message, &body_bg_color[0], font_hover_color); + mui->box_message, &body_bg_color[0], font_hover_color); free(mui->box_message); mui->box_message = NULL; @@ -1795,10 +1795,10 @@ static float materialui_get_scroll(materialui_handle_t *mui) for (i = 0; i < selection; i++) { materialui_node_t *node = (materialui_node_t*) - file_list_get_userdata_at_offset(list, i); + file_list_get_userdata_at_offset(list, i); if (node) - sum += node->line_height; + sum += node->line_height; } if (sum < half) @@ -1808,7 +1808,7 @@ static float materialui_get_scroll(materialui_handle_t *mui) } /* The navigation pointer has been updated (for example by pressing up or down -on the keyboard). We use this function to animate the scroll. */ + on the keyboard). We use this function to animate the scroll. */ static void materialui_navigation_set(void *data, bool scroll) { menu_animation_ctx_entry_t entry; @@ -1953,7 +1953,7 @@ static void materialui_preswitch_tabs(materialui_handle_t *mui, unsigned action) } /* This callback is not caching anything. We use it to navigate the tabs -with the keyboard */ + with the keyboard */ static void materialui_list_cache(void *data, enum menu_list_type type, unsigned action) { @@ -2003,7 +2003,7 @@ static void materialui_list_cache(void *data, } /* A new list has been pushed. We use this callback to customize a few lists for -this menu driver */ + this menu driver */ static int materialui_list_push(void *data, void *userdata, menu_displaylist_info_t *info, unsigned type) { @@ -2176,7 +2176,7 @@ static size_t materialui_list_get_selection(void *data) } /* The pointer or the mouse is pressed down. We use this callback to -highlight the entry that has been pressed */ + highlight the entry that has been pressed */ static int materialui_pointer_down(void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, @@ -2209,11 +2209,11 @@ static int materialui_pointer_down(void *userdata, for (ii = 0; ii < entries_end; ii++) { materialui_node_t *node = (materialui_node_t*) - file_list_get_userdata_at_offset(list, ii); + file_list_get_userdata_at_offset(list, ii); if (y > (-mui->scroll_y + header_height + node->y) - && y < (-mui->scroll_y + header_height + node->y + node->line_height) - ) + && y < (-mui->scroll_y + header_height + node->y + node->line_height) + ) menu_navigation_set_selection(ii); } @@ -2224,9 +2224,9 @@ static int materialui_pointer_down(void *userdata, } /* The pointer or the left mouse button has been released. -If we clicked on the header, we perform a cancel action. -If we clicked on the tabs, we switch to a new list. -If we clicked on a menu entry, we call the entry action callback. */ + If we clicked on the header, we perform a cancel action. + If we clicked on the tabs, we switch to a new list. + If we clicked on a menu entry, we call the entry action callback. */ static int materialui_pointer_up(void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, @@ -2278,11 +2278,11 @@ static int materialui_pointer_up(void *userdata, for (ii = 0; ii < entries_end; ii++) { materialui_node_t *node = (materialui_node_t*) - file_list_get_userdata_at_offset(list, ii); + file_list_get_userdata_at_offset(list, ii); if (y > (-mui->scroll_y + header_height + node->y) - && y < (-mui->scroll_y + header_height + node->y + node->line_height) - ) + && y < (-mui->scroll_y + header_height + node->y + node->line_height) + ) { if (ptr == ii && cbs && cbs->action_select) return menu_entry_action(entry, (unsigned)ii, MENU_ACTION_SELECT); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 3926bf1a1c..217eb5b6b5 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -105,9 +105,11 @@ enum #ifdef HAVE_NETWORKING XMB_TEXTURE_NETPLAY, XMB_TEXTURE_ROOM, -/* stub these out until we have the icons +#if 0 + /* stub these out until we have the icons */ XMB_TEXTURE_ROOM_LAN, - XMB_TEXTURE_ROOM_MITM,*/ + XMB_TEXTURE_ROOM_MITM, +#endif #endif #ifdef HAVE_IMAGEVIEWER XMB_TEXTURE_IMAGES, @@ -317,9 +319,9 @@ static float item_color[] = { float gradient_dark_purple[16] = { - 20/255.0, 13/255.0, 20/255.0, 1.0, - 20/255.0, 13/255.0, 20/255.0, 1.0, - 92/255.0, 44/255.0, 92/255.0, 1.0, + 20/255.0, 13/255.0, 20/255.0, 1.0, + 20/255.0, 13/255.0, 20/255.0, 1.0, + 92/255.0, 44/255.0, 92/255.0, 1.0, 148/255.0, 90/255.0, 148/255.0, 1.0, }; @@ -345,24 +347,24 @@ float gradient_legacy_red[16] = { }; float gradient_electric_blue[16] = { - 1/255.0, 2/255.0, 67/255.0, 1.0, - 1/255.0, 73/255.0, 183/255.0, 1.0, - 1/255.0, 93/255.0, 194/255.0, 1.0, - 3/255.0, 162/255.0, 254/255.0, 1.0, + 1/255.0, 2/255.0, 67/255.0, 1.0, + 1/255.0, 73/255.0, 183/255.0, 1.0, + 1/255.0, 93/255.0, 194/255.0, 1.0, + 3/255.0, 162/255.0, 254/255.0, 1.0, }; float gradient_apple_green[16] = { 102/255.0, 134/255.0, 58/255.0, 1.0, 122/255.0, 131/255.0, 52/255.0, 1.0, - 82/255.0, 101/255.0, 35/255.0, 1.0, - 63/255.0, 95/255.0, 30/255.0, 1.0, + 82/255.0, 101/255.0, 35/255.0, 1.0, + 63/255.0, 95/255.0, 30/255.0, 1.0, }; float gradient_undersea[16] = { - 23/255.0, 18/255.0, 41/255.0, 1.0, - 30/255.0, 72/255.0, 114/255.0, 1.0, - 52/255.0, 88/255.0, 110/255.0, 1.0, - 69/255.0, 125/255.0, 140/255.0, 1.0, + 23/255.0, 18/255.0, 41/255.0, 1.0, + 30/255.0, 72/255.0, 114/255.0, 1.0, + 52/255.0, 88/255.0, 110/255.0, 1.0, + 69/255.0, 125/255.0, 140/255.0, 1.0, }; @@ -445,15 +447,15 @@ static xmb_node_t *xmb_alloc_node(void) static void xmb_free_node(xmb_node_t *node) { - if (!node) - return; + if (!node) + return; - if (node->fullpath) - free(node->fullpath); + if (node->fullpath) + free(node->fullpath); - node->fullpath = NULL; + node->fullpath = NULL; - free(node); + free(node); } /** @@ -908,8 +910,8 @@ static void xmb_update_thumbnail_path(void *data, unsigned i, char pos) file_list_get_userdata_at_offset(selection_buf, i); if (!string_is_empty(node->fullpath) && - (pos == 'R' || (pos == 'L' && string_is_equal(xmb_thumbnails_ident('R'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))))) + (pos == 'R' || (pos == 'L' && string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))))) { if (!string_is_empty(entry.path)) fill_pathname_join( @@ -1099,28 +1101,28 @@ static void xmb_update_thumbnail_image(void *data) return; if (!(string_is_empty(xmb->thumbnail_file_path))) - { - if (filestream_exists(xmb->thumbnail_file_path)) - task_push_image_load(xmb->thumbnail_file_path, - menu_display_handle_thumbnail_upload, NULL); - else - xmb->thumbnail = 0; + { + if (filestream_exists(xmb->thumbnail_file_path)) + task_push_image_load(xmb->thumbnail_file_path, + menu_display_handle_thumbnail_upload, NULL); + else + xmb->thumbnail = 0; - free(xmb->thumbnail_file_path); - xmb->thumbnail_file_path = NULL; - } + free(xmb->thumbnail_file_path); + xmb->thumbnail_file_path = NULL; + } if (!(string_is_empty(xmb->left_thumbnail_file_path))) - { - if (filestream_exists(xmb->left_thumbnail_file_path)) - task_push_image_load(xmb->left_thumbnail_file_path, - menu_display_handle_left_thumbnail_upload, NULL); - else - xmb->left_thumbnail = 0; + { + if (filestream_exists(xmb->left_thumbnail_file_path)) + task_push_image_load(xmb->left_thumbnail_file_path, + menu_display_handle_left_thumbnail_upload, NULL); + else + xmb->left_thumbnail = 0; - free(xmb->left_thumbnail_file_path); - xmb->left_thumbnail_file_path = NULL; - } + free(xmb->left_thumbnail_file_path); + xmb->left_thumbnail_file_path = NULL; + } } static void xmb_set_thumbnail_system(void *data, char*s, size_t len) @@ -1232,22 +1234,22 @@ static void xmb_selection_pointer_changed( ia = xmb->items_active_alpha; iz = xmb->items_active_zoom; if (!string_is_equal(thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) || !string_is_equal(lft_thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) || !string_is_equal(lft_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { if ((xmb_system_tab > XMB_SYSTEM_TAB_SETTINGS && depth == 1) || - (xmb_system_tab < XMB_SYSTEM_TAB_SETTINGS && depth == 4)) + (xmb_system_tab < XMB_SYSTEM_TAB_SETTINGS && depth == 4)) { if (!string_is_empty(entry.path)) xmb_set_thumbnail_content(xmb, entry.path, 0 /* will be ignored */); if (!string_is_equal(thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { xmb_update_thumbnail_path(xmb, i, 'R'); xmb_update_thumbnail_image(xmb); } if (!string_is_equal(lft_thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { xmb_update_thumbnail_path(xmb, i, 'L'); xmb_update_thumbnail_image(xmb); @@ -1255,18 +1257,18 @@ static void xmb_selection_pointer_changed( } else if (((entry_type == FILE_TYPE_IMAGE || entry_type == FILE_TYPE_IMAGEVIEWER || entry_type == FILE_TYPE_RDB || entry_type == FILE_TYPE_RDB_ENTRY) - && xmb_system_tab <= XMB_SYSTEM_TAB_SETTINGS)) + && xmb_system_tab <= XMB_SYSTEM_TAB_SETTINGS)) { if (!string_is_empty(entry.path)) xmb_set_thumbnail_content(xmb, entry.path, 0 /* will be ignored */); if (!string_is_equal(thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { xmb_update_thumbnail_path(xmb, i, 'R'); xmb_update_thumbnail_image(xmb); } else if (!string_is_equal(lft_thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { xmb_update_thumbnail_path(xmb, i, 'L'); xmb_update_thumbnail_image(xmb); @@ -1276,13 +1278,13 @@ static void xmb_selection_pointer_changed( { xmb_reset_thumbnail_content(xmb); if (!string_is_equal(thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { xmb_update_thumbnail_path(xmb, i, 'R'); xmb_update_thumbnail_image(xmb); } else if (!string_is_equal(lft_thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { xmb_update_thumbnail_path(xmb, i, 'L'); xmb_update_thumbnail_image(xmb); @@ -1295,7 +1297,7 @@ static void xmb_selection_pointer_changed( if ( (!allow_animations) || (real_iy < -threshold - || real_iy > height+threshold)) + || real_iy > height+threshold)) { node->alpha = node->label_alpha = ia; node->y = iy; @@ -1606,19 +1608,19 @@ static void xmb_list_switch_new(xmb_handle_t *xmb, fill_pathname_application_special(path, path_size, APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_BG); - if(!string_is_equal(path, xmb->bg_file_path)) - { - if(filestream_exists(path)) - { - task_push_image_load(path, + if(!string_is_equal(path, xmb->bg_file_path)) + { + if(filestream_exists(path)) + { + task_push_image_load(path, menu_display_handle_wallpaper_upload, NULL); - if (!string_is_empty(xmb->bg_file_path)) - free(xmb->bg_file_path); - xmb->bg_file_path = strdup(path); - } - } + if (!string_is_empty(xmb->bg_file_path)) + free(xmb->bg_file_path); + xmb->bg_file_path = strdup(path); + } + } - free(path); + free(path); } end = file_list_get_size(list); @@ -1987,7 +1989,7 @@ static void xmb_context_reset_horizontal_list( malloc(PATH_MAX_LENGTH * sizeof(char)); iconpath[0] = sysname[0] = - texturepath[0] = content_texturepath[0] = '\0'; + texturepath[0] = content_texturepath[0] = '\0'; fill_pathname_base_noext(sysname, path, sizeof(sysname)); @@ -2324,7 +2326,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, case MENU_ROOM: return xmb->textures.list[XMB_TEXTURE_ROOM]; #if 0 - /* stub these out until we have the icons */ + /* stub these out until we have the icons */ case MENU_ROOM_LAN: return xmb->textures.list[XMB_TEXTURE_ROOM_LAN]; case MENU_ROOM_MITM: @@ -2494,7 +2496,7 @@ static int xmb_draw_item( { } else - do_draw_text = true; + do_draw_text = true; } else do_draw_text = true; @@ -2508,7 +2510,7 @@ static int xmb_draw_item( (thumb_ident, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) && xmb->thumbnail) || - (!string_is_equal + (!string_is_equal (left_thumb_ident, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) && xmb->left_thumbnail @@ -2689,7 +2691,7 @@ static void xmb_draw_items( if (list == xmb->selection_buf_old) { xmb_node_t *node = (xmb_node_t*) - file_list_get_userdata_at_offset(list, current); + file_list_get_userdata_at_offset(list, current); if (node && (uint8_t)(255 * node->alpha) == 0) return; @@ -2986,7 +2988,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) xmb->raster_block2.carr.coords.vertices = 0; menu_display_set_alpha(coord_black, MIN( - (float)video_info->xmb_alpha_factor/100, xmb->alpha)); + (float)video_info->xmb_alpha_factor/100, xmb->alpha)); menu_display_set_alpha(coord_white, xmb->alpha); xmb_draw_bg( @@ -3046,31 +3048,31 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) if (xmb->savestate_thumbnail) { xmb_draw_thumbnail(video_info, - xmb, &coord_white[0], width, height, - xmb->margins_screen_left * scale_mod[5] + - xmb->icon_spacing_horizontal + pseudo_font_length, - xmb->margins_screen_top + xmb->icon_size + - xmb->savestate_thumbnail_height * scale_mod[4], - xmb->savestate_thumbnail_width * scale_mod[4], - xmb->savestate_thumbnail_height * scale_mod[4], - xmb->savestate_thumbnail); + xmb, &coord_white[0], width, height, + xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + pseudo_font_length, + xmb->margins_screen_top + xmb->icon_size + + xmb->savestate_thumbnail_height * scale_mod[4], + xmb->savestate_thumbnail_width * scale_mod[4], + xmb->savestate_thumbnail_height * scale_mod[4], + xmb->savestate_thumbnail); } /* Right thumbnail big size */ if (!settings->bools.menu_xmb_vertical_thumbnails || - (settings->bools.menu_xmb_vertical_thumbnails && !xmb->left_thumbnail)) + (settings->bools.menu_xmb_vertical_thumbnails && !xmb->left_thumbnail)) { /* Do not draw the right thumbnail if there is no space available */ if (((xmb->margins_screen_top + xmb->icon_size + min_thumb_size) <= height) && - ((xmb->margins_screen_left * scale_mod[5] + - xmb->icon_spacing_horizontal + - pseudo_font_length + min_thumb_size) <= width)) + ((xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) { if (xmb->thumbnail - && !string_is_equal(xmb_thumbnails_ident('R'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + && !string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { /* Limit thumbnail width */ @@ -3080,11 +3082,11 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) - (xmb->margins_screen_left * scale_mod[5]) - xmb->icon_spacing_horizontal - pseudo_font_length; - #ifdef XMB_DEBUG +#ifdef XMB_DEBUG RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", xmb->thumbnail_width, xmb->thumbnail_height); RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif +#endif if (xmb->thumbnail_width * scale_mod[4] > thumb_max_width) { @@ -3129,7 +3131,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Do not draw the left thumbnail if there is no space available */ if (!settings->bools.menu_xmb_vertical_thumbnails && (xmb->margins_screen_top + xmb->icon_size * - (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) + (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) <= (float)height) { /* Left Thumbnail in the left margin */ @@ -3144,11 +3146,11 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) float left_thumb_height = 0.0f; float thumb_max_width = xmb->icon_size * 3.4; - #ifdef XMB_DEBUG +#ifdef XMB_DEBUG RARCH_LOG("[XMB left thumbnail] width: %.2f, height: %.2f\n", xmb->left_thumbnail_width, xmb->left_thumbnail_height); RARCH_LOG("[XMB left thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif +#endif if (xmb->left_thumbnail_width * scale_mod[4] > thumb_max_width) { @@ -3187,7 +3189,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) (xmb->icon_size / 6) + ((thumb_max_width - left_thumb_width) / 2), xmb->margins_screen_top + xmb->icon_size * - (!(xmb->depth == 1)? 2.1 : 1) + left_thumb_height, + (!(xmb->depth == 1)? 2.1 : 1) + left_thumb_height, left_thumb_width, left_thumb_height, xmb->left_thumbnail); } @@ -3200,13 +3202,13 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) if (((xmb->margins_screen_top + xmb->icon_size + min_thumb_size) <= height) && - ((xmb->margins_screen_left * scale_mod[5] + - xmb->icon_spacing_horizontal + - pseudo_font_length + min_thumb_size) <= width)) + ((xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) { if (xmb->left_thumbnail - && !string_is_equal(xmb_thumbnails_ident('L'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + && !string_is_equal(xmb_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { /* Limit left thumbnail width */ @@ -3216,11 +3218,11 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) - (xmb->margins_screen_left * scale_mod[5]) - xmb->icon_spacing_horizontal - pseudo_font_length; - #ifdef XMB_DEBUG +#ifdef XMB_DEBUG RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", xmb->thumbnail_width, xmb->thumbnail_height); RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif +#endif if (xmb->left_thumbnail_width * scale_mod[4] > thumb_max_width) { @@ -3238,16 +3240,16 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Limit left thumbnail height to screen height + margin. */ if (xmb->margins_screen_top + xmb->icon_size + left_thumb_height >= - ((float)height * under_thumb_margin)) + ((float)height * under_thumb_margin)) { left_thumb_width = left_thumb_width * ((((float)height * under_thumb_margin) - - xmb->margins_screen_top - xmb->icon_size) / - left_thumb_height); + xmb->margins_screen_top - xmb->icon_size) / + left_thumb_height); left_thumb_height = left_thumb_height * ((((float)height * under_thumb_margin) - - xmb->margins_screen_top - xmb->icon_size) / - left_thumb_height); + xmb->margins_screen_top - xmb->icon_size) / + left_thumb_height); } xmb_draw_thumbnail(video_info, @@ -3390,7 +3392,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Horizontal tab icons */ for (i = 0; i <= xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL) - + xmb->system_tab_end; i++) + + xmb->system_tab_end; i++) { xmb_node_t *node = xmb_get_node(xmb, i); @@ -3405,9 +3407,9 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) math_matrix_4x4 mymat; uintptr_t texture = node->icon; float x = xmb->x + xmb->categories_x_pos + - xmb->margins_screen_left + - xmb->icon_spacing_horizontal - * (i + 1) - xmb->icon_size / 2.0; + xmb->margins_screen_left + + xmb->icon_spacing_horizontal + * (i + 1) - xmb->icon_size / 2.0; float y = xmb->margins_screen_top + xmb->icon_size / 2.0; float rotation = 0; @@ -3444,14 +3446,14 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) { /* Do not draw the right thumbnail if there is no space available */ if (((xmb->margins_screen_top + - xmb->icon_size + min_thumb_size) <= height) && - ((xmb->margins_screen_left * scale_mod[5] + - xmb->icon_spacing_horizontal + - pseudo_font_length + min_thumb_size) <= width)) + xmb->icon_size + min_thumb_size) <= height) && + ((xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) { if (xmb->thumbnail && - !string_is_equal(xmb_thumbnails_ident('R'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + !string_is_equal(xmb_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { /* Limit right thumbnail width */ @@ -3461,11 +3463,11 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) (xmb->margins_screen_left * scale_mod[5]) - xmb->icon_spacing_horizontal - pseudo_font_length; - #ifdef XMB_DEBUG +#ifdef XMB_DEBUG RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", - xmb->thumbnail_width, xmb->thumbnail_height); + xmb->thumbnail_width, xmb->thumbnail_height); RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif +#endif if (xmb->thumbnail_width * scale_mod[4] > thumb_max_width) { @@ -3483,39 +3485,39 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Limit right thumbnail height to usable area. */ if (thumb_height >= - ((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) + ((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) { thumb_width = thumb_width * ((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) / - thumb_height); + thumb_height); thumb_height = thumb_height * ((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) / - thumb_height); + thumb_height); } xmb_draw_thumbnail(video_info, - xmb, &coord_white[0], width, height, - (float)width - (xmb->icon_size / 6) - thumb_max_width + - ((thumb_max_width - thumb_width) / 2), - xmb->icon_size + ((((float)height / 2 - - (xmb->icon_size + (xmb->icon_size/12))) - thumb_height) / 2) + - thumb_height, - thumb_width, thumb_height, - xmb->thumbnail); + xmb, &coord_white[0], width, height, + (float)width - (xmb->icon_size / 6) - thumb_max_width + + ((thumb_max_width - thumb_width) / 2), + xmb->icon_size + ((((float)height / 2 - + (xmb->icon_size + (xmb->icon_size/12))) - thumb_height) / 2) + + thumb_height, + thumb_width, thumb_height, + xmb->thumbnail); } } /* Do not draw the left thumbnail if there is no space available */ if (((xmb->margins_screen_top + - xmb->icon_size + min_thumb_size) <= height) && - ((xmb->margins_screen_left * scale_mod[5] + - xmb->icon_spacing_horizontal + - pseudo_font_length + min_thumb_size) <= width)) + xmb->icon_size + min_thumb_size) <= height) && + ((xmb->margins_screen_left * scale_mod[5] + + xmb->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) { if (xmb->left_thumbnail && - !string_is_equal(xmb_thumbnails_ident('L'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + !string_is_equal(xmb_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) { /* Limit left thumbnail width */ @@ -3525,11 +3527,11 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) (xmb->margins_screen_left * scale_mod[5]) - xmb->icon_spacing_horizontal - pseudo_font_length; - #ifdef XMB_DEBUG +#ifdef XMB_DEBUG RARCH_LOG("[XMB left thumbnail] width: %.2f, height: %.2f\n", - xmb->left_thumbnail_width, xmb->left_thumbnail_height); + xmb->left_thumbnail_width, xmb->left_thumbnail_height); RARCH_LOG("[XMB left thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif +#endif if (xmb->left_thumbnail_width * scale_mod[4] > thumb_max_width) { @@ -3547,26 +3549,26 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Limit left thumbnail height to usable area. */ if (left_thumb_height >= - ((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) + ((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) { left_thumb_width = left_thumb_width * ((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) / - left_thumb_height); + left_thumb_height); left_thumb_height = left_thumb_height * ((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) / - left_thumb_height); + left_thumb_height); } xmb_draw_thumbnail(video_info, - xmb, &coord_white[0], width, height, - (float)width - (xmb->icon_size / 6) - thumb_max_width + + xmb, &coord_white[0], width, height, + (float)width - (xmb->icon_size / 6) - thumb_max_width + ((thumb_max_width - left_thumb_width) / 2), - xmb->icon_size + + xmb->icon_size + (((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) + (((((float)height - ((xmb->icon_size / 6) * 2) - xmb->icon_size) / 2) - - left_thumb_height) / 2) + left_thumb_height, - left_thumb_width, left_thumb_height, - xmb->left_thumbnail); + left_thumb_height) / 2) + left_thumb_height, + left_thumb_width, left_thumb_height, + xmb->left_thumbnail); } } } @@ -3918,23 +3920,24 @@ static void *xmb_init(void **userdata, bool video_is_threaded) /* scaling multiplier formulas made from these values: */ /* xmb_scale 50 = {2.5, 2.5, 2, 1.7, 2.5, 4, 2.4, 2.5} */ /* xmb_scale 75 = { 2, 1.6, 1.6, 1.4, 1.5, 2.3, 1.9, 1.3} */ + if (scale_value < 100) { - /* text length & word wrap (base 35 apply to file browser, 1st column) */ + /* text length & word wrap (base 35 apply to file browser, 1st column) */ scale_mod[0] = -0.03 * scale_value + 4.083; - /* playlist text length when thumbnail is ON (small, base 40) */ + /* playlist text length when thumbnail is ON (small, base 40) */ scale_mod[1] = -0.03 * scale_value + 3.95; - /* playlist text length when thumbnail is OFF (large, base 70) */ + /* playlist text length when thumbnail is OFF (large, base 70) */ scale_mod[2] = -0.02 * scale_value + 3.033; - /* sub-label length & word wrap */ + /* sub-label length & word wrap */ scale_mod[3] = -0.014 * scale_value + 2.416; - /* thumbnail size & vertical margin from top */ + /* thumbnail size & vertical margin from top */ scale_mod[4] = -0.03 * scale_value + 3.916; - /* thumbnail horizontal left margin (horizontal positioning) */ + /* thumbnail horizontal left margin (horizontal positioning) */ scale_mod[5] = -0.06 * scale_value + 6.933; - /* margin before 2nd column start (shaders parameters, cheats...) */ + /* margin before 2nd column start (shaders parameters, cheats...) */ scale_mod[6] = -0.028 * scale_value + 3.866; - /* text length & word wrap (base 35 apply to 2nd column in cheats, shaders, etc) */ + /* text length & word wrap (base 35 apply to 2nd column in cheats, shaders, etc) */ scale_mod[7] = 134.179 * pow(scale_value, -1.0778); for (i = 0; i < 8; i++) @@ -4249,12 +4252,13 @@ static const char *xmb_texture_path(unsigned id) return "netplay.png"; case XMB_TEXTURE_ROOM: return "room.png"; - /* stub these out until we have the icons +#if 0 + /* stub these out until we have the icons */ case XMB_TEXTURE_ROOM_LAN: return "room_lan.png"; case XMB_TEXTURE_ROOM_MITM: return "room_mitm.png"; - */ +#endif #endif case XMB_TEXTURE_KEY: return "key.png"; @@ -4745,7 +4749,7 @@ static void xmb_toggle(void *userdata, bool menu_on) static int deferred_push_content_actions(menu_displaylist_info_t *info) { if (!menu_displaylist_ctl( - DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info)) + DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info)) return -1; menu_displaylist_process(info); menu_displaylist_info_free(info); From 6f3003554e1012176559a7181a48ceac93b00b61 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 12:59:28 +0200 Subject: [PATCH 410/517] (D3D10/D3D11/D3D12) Don't load menu pipeline shaders unless XMB is selected --- gfx/drivers/d3d10.c | 85 ++++++++-------- gfx/drivers/d3d11.c | 85 ++++++++-------- gfx/drivers/d3d12.c | 234 ++++++++++++++++++++++---------------------- 3 files changed, 207 insertions(+), 197 deletions(-) diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 6938b2151a..681defe247 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -801,68 +801,71 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i goto error; } + if (string_is_equal(settings->arrays.menu_driver, "xmb")) { - D3D10_INPUT_ELEMENT_DESC desc[] = { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, - }; + { + D3D10_INPUT_ELEMENT_DESC desc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; - static const char ribbon[] = + static const char ribbon[] = #include "d3d_shaders/ribbon_sm4.hlsl.h" ; - static const char ribbon_simple[] = + static const char ribbon_simple[] = #include "d3d_shaders/ribbon_simple_sm4.hlsl.h" ; - if (!d3d10_init_shader( - d3d10->device, ribbon, sizeof(ribbon), NULL, "VSMain", "PSMain", NULL, desc, - countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU])) - goto error; + if (!d3d10_init_shader( + d3d10->device, ribbon, sizeof(ribbon), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU])) + goto error; - if (!d3d10_init_shader( - d3d10->device, ribbon_simple, sizeof(ribbon_simple), NULL, "VSMain", "PSMain", NULL, - desc, countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_2])) - goto error; - } + if (!d3d10_init_shader( + d3d10->device, ribbon_simple, sizeof(ribbon_simple), NULL, "VSMain", "PSMain", NULL, + desc, countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_2])) + goto error; + } - { - D3D10_INPUT_ELEMENT_DESC desc[] = { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, position), - D3D10_INPUT_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, texcoord), - D3D10_INPUT_PER_VERTEX_DATA, 0 }, - }; + { + D3D10_INPUT_ELEMENT_DESC desc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, position), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d10_vertex_t, texcoord), + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; - static const char simple_snow[] = + static const char simple_snow[] = #include "d3d_shaders/simple_snow_sm4.hlsl.h" ; - static const char snow[] = + static const char snow[] = #include "d3d_shaders/snow_sm4.hlsl.h" ; - static const char bokeh[] = + static const char bokeh[] = #include "d3d_shaders/bokeh_sm4.hlsl.h" ; - static const char snowflake[] = + static const char snowflake[] = #include "d3d_shaders/snowflake_sm4.hlsl.h" ; - if (!d3d10_init_shader( - d3d10->device, simple_snow, sizeof(simple_snow), NULL, "VSMain", "PSMain", NULL, - desc, countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_3])) - goto error; - if (!d3d10_init_shader( - d3d10->device, snow, sizeof(snow), NULL, "VSMain", "PSMain", NULL, desc, - countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_4])) - goto error; + if (!d3d10_init_shader( + d3d10->device, simple_snow, sizeof(simple_snow), NULL, "VSMain", "PSMain", NULL, + desc, countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_3])) + goto error; + if (!d3d10_init_shader( + d3d10->device, snow, sizeof(snow), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_4])) + goto error; - if (!d3d10_init_shader( - d3d10->device, bokeh, sizeof(bokeh), NULL, "VSMain", "PSMain", NULL, desc, - countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_5])) - goto error; + if (!d3d10_init_shader( + d3d10->device, bokeh, sizeof(bokeh), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_5])) + goto error; - if (!d3d10_init_shader( - d3d10->device, snowflake, sizeof(snowflake), NULL, "VSMain", "PSMain", NULL, desc, - countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_6])) - goto error; + if (!d3d10_init_shader( + d3d10->device, snowflake, sizeof(snowflake), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d10->shaders[VIDEO_SHADER_MENU_6])) + goto error; + } } { diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index bdac1dca9f..8a5a6f1f7d 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -850,68 +850,71 @@ d3d11_gfx_init(const video_info_t* video, const input_driver_t** input, void** i goto error; } + if (string_is_equal(settings->arrays.menu_driver, "xmb")) { - D3D11_INPUT_ELEMENT_DESC desc[] = { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - }; + { + D3D11_INPUT_ELEMENT_DESC desc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; - static const char ribbon[] = + static const char ribbon[] = #include "d3d_shaders/ribbon_sm4.hlsl.h" ; - static const char ribbon_simple[] = + static const char ribbon_simple[] = #include "d3d_shaders/ribbon_simple_sm4.hlsl.h" ; - if (!d3d11_init_shader( - d3d11->device, ribbon, sizeof(ribbon), NULL, "VSMain", "PSMain", NULL, desc, - countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU])) - goto error; + if (!d3d11_init_shader( + d3d11->device, ribbon, sizeof(ribbon), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU])) + goto error; - if (!d3d11_init_shader( - d3d11->device, ribbon_simple, sizeof(ribbon_simple), NULL, "VSMain", "PSMain", NULL, - desc, countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_2])) - goto error; - } + if (!d3d11_init_shader( + d3d11->device, ribbon_simple, sizeof(ribbon_simple), NULL, "VSMain", "PSMain", NULL, + desc, countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_2])) + goto error; + } - { - D3D11_INPUT_ELEMENT_DESC desc[] = { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d11_vertex_t, position), - D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d11_vertex_t, texcoord), - D3D11_INPUT_PER_VERTEX_DATA, 0 }, - }; + { + D3D11_INPUT_ELEMENT_DESC desc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d11_vertex_t, position), + D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d11_vertex_t, texcoord), + D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; - static const char simple_snow[] = + static const char simple_snow[] = #include "d3d_shaders/simple_snow_sm4.hlsl.h" ; - static const char snow[] = + static const char snow[] = #include "d3d_shaders/snow_sm4.hlsl.h" ; - static const char bokeh[] = + static const char bokeh[] = #include "d3d_shaders/bokeh_sm4.hlsl.h" ; - static const char snowflake[] = + static const char snowflake[] = #include "d3d_shaders/snowflake_sm4.hlsl.h" ; - if (!d3d11_init_shader( - d3d11->device, simple_snow, sizeof(simple_snow), NULL, "VSMain", "PSMain", NULL, - desc, countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_3])) - goto error; - if (!d3d11_init_shader( - d3d11->device, snow, sizeof(snow), NULL, "VSMain", "PSMain", NULL, desc, - countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_4])) - goto error; + if (!d3d11_init_shader( + d3d11->device, simple_snow, sizeof(simple_snow), NULL, "VSMain", "PSMain", NULL, + desc, countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_3])) + goto error; + if (!d3d11_init_shader( + d3d11->device, snow, sizeof(snow), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_4])) + goto error; - if (!d3d11_init_shader( - d3d11->device, bokeh, sizeof(bokeh), NULL, "VSMain", "PSMain", NULL, desc, - countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_5])) - goto error; + if (!d3d11_init_shader( + d3d11->device, bokeh, sizeof(bokeh), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_5])) + goto error; - if (!d3d11_init_shader( - d3d11->device, snowflake, sizeof(snowflake), NULL, "VSMain", "PSMain", NULL, desc, - countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_6])) - goto error; + if (!d3d11_init_shader( + d3d11->device, snowflake, sizeof(snowflake), NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d11->shaders[VIDEO_SHADER_MENU_6])) + goto error; + } } { diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 160232e818..a7b6532984 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -544,6 +544,7 @@ static bool d3d12_gfx_init_pipelines(d3d12_video_t* d3d12) D3DBlob ps_code = NULL; D3DBlob gs_code = NULL; D3DBlob cs_code = NULL; + settings_t * settings = config_get_ptr(); D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = { d3d12->desc.rootSignature }; desc.BlendState.RenderTarget[0] = d3d12_blend_enable_desc; @@ -643,134 +644,137 @@ static bool d3d12_gfx_init_pipelines(d3d12_video_t* d3d12) gs_code = NULL; } + if (string_is_equal(settings->arrays.menu_driver, "xmb")) { - static const char simple_snow[] = -#include "d3d_shaders/simple_snow_sm4.hlsl.h" - ; - static const char snow[] = -#include "d3d_shaders/snow_sm4.hlsl.h" - ; - static const char bokeh[] = -#include "d3d_shaders/bokeh_sm4.hlsl.h" - ; - static const char snowflake[] = -#include "d3d_shaders/snowflake_sm4.hlsl.h" - ; - - D3D12_INPUT_ELEMENT_DESC inputElementDesc[] = { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d12_vertex_t, position), - D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d12_vertex_t, texcoord), - D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - }; - - desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - desc.InputLayout.pInputElementDescs = inputElementDesc; - desc.InputLayout.NumElements = countof(inputElementDesc); - - if (!d3d_compile(simple_snow, sizeof(simple_snow), NULL, "VSMain", "vs_5_0", &vs_code)) - goto error; - if (!d3d_compile(simple_snow, sizeof(simple_snow), NULL, "PSMain", "ps_5_0", &ps_code)) - goto error; - - if (!d3d12_init_pipeline( - d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_3])) - goto error; - - Release(vs_code); - Release(ps_code); - vs_code = NULL; - ps_code = NULL; - - if (!d3d_compile(snow, sizeof(snow), NULL, "VSMain", "vs_5_0", &vs_code)) - goto error; - if (!d3d_compile(snow, sizeof(snow), NULL, "PSMain", "ps_5_0", &ps_code)) - goto error; - - if (!d3d12_init_pipeline( - d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_4])) - goto error; - - Release(vs_code); - Release(ps_code); - vs_code = NULL; - ps_code = NULL; - - if (!d3d_compile(bokeh, sizeof(bokeh), NULL, "VSMain", "vs_5_0", &vs_code)) - goto error; - if (!d3d_compile(bokeh, sizeof(bokeh), NULL, "PSMain", "ps_5_0", &ps_code)) - goto error; - - if (!d3d12_init_pipeline( - d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_5])) - goto error; - - Release(vs_code); - Release(ps_code); - vs_code = NULL; - ps_code = NULL; - - if (!d3d_compile(snowflake, sizeof(snowflake), NULL, "VSMain", "vs_5_0", &vs_code)) - goto error; - if (!d3d_compile(snowflake, sizeof(snowflake), NULL, "PSMain", "ps_5_0", &ps_code)) - goto error; - - if (!d3d12_init_pipeline( - d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_6])) - goto error; - - Release(vs_code); - Release(ps_code); - vs_code = NULL; - ps_code = NULL; - } - - { - static const char ribbon[] = + { + static const char ribbon[] = #include "d3d_shaders/ribbon_sm4.hlsl.h" ; - static const char ribbon_simple[] = + static const char ribbon_simple[] = #include "d3d_shaders/ribbon_simple_sm4.hlsl.h" ; - D3D12_INPUT_ELEMENT_DESC inputElementDesc[] = { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, - D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - }; + D3D12_INPUT_ELEMENT_DESC inputElementDesc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, + D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + }; - desc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_ONE; - desc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_ONE; - desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - desc.InputLayout.pInputElementDescs = inputElementDesc; - desc.InputLayout.NumElements = countof(inputElementDesc); + desc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_ONE; + desc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_ONE; + desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc.InputLayout.pInputElementDescs = inputElementDesc; + desc.InputLayout.NumElements = countof(inputElementDesc); - if (!d3d_compile(ribbon, sizeof(ribbon), NULL, "VSMain", "vs_5_0", &vs_code)) - goto error; - if (!d3d_compile(ribbon, sizeof(ribbon), NULL, "PSMain", "ps_5_0", &ps_code)) - goto error; + if (!d3d_compile(ribbon, sizeof(ribbon), NULL, "VSMain", "vs_5_0", &vs_code)) + goto error; + if (!d3d_compile(ribbon, sizeof(ribbon), NULL, "PSMain", "ps_5_0", &ps_code)) + goto error; - if (!d3d12_init_pipeline( - d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU])) - goto error; + if (!d3d12_init_pipeline( + d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU])) + goto error; - Release(vs_code); - Release(ps_code); - vs_code = NULL; - ps_code = NULL; + Release(vs_code); + Release(ps_code); + vs_code = NULL; + ps_code = NULL; - if (!d3d_compile(ribbon_simple, sizeof(ribbon_simple), NULL, "VSMain", "vs_5_0", &vs_code)) - goto error; - if (!d3d_compile(ribbon_simple, sizeof(ribbon_simple), NULL, "PSMain", "ps_5_0", &ps_code)) - goto error; + if (!d3d_compile(ribbon_simple, sizeof(ribbon_simple), NULL, "VSMain", "vs_5_0", &vs_code)) + goto error; + if (!d3d_compile(ribbon_simple, sizeof(ribbon_simple), NULL, "PSMain", "ps_5_0", &ps_code)) + goto error; - if (!d3d12_init_pipeline( - d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_2])) - goto error; + if (!d3d12_init_pipeline( + d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_2])) + goto error; - Release(vs_code); - Release(ps_code); - vs_code = NULL; - ps_code = NULL; + Release(vs_code); + Release(ps_code); + vs_code = NULL; + ps_code = NULL; + } + + { + static const char simple_snow[] = +#include "d3d_shaders/simple_snow_sm4.hlsl.h" + ; + static const char snow[] = +#include "d3d_shaders/snow_sm4.hlsl.h" + ; + static const char bokeh[] = +#include "d3d_shaders/bokeh_sm4.hlsl.h" + ; + static const char snowflake[] = +#include "d3d_shaders/snowflake_sm4.hlsl.h" + ; + + D3D12_INPUT_ELEMENT_DESC inputElementDesc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d12_vertex_t, position), + D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d12_vertex_t, texcoord), + D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + }; + + desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc.InputLayout.pInputElementDescs = inputElementDesc; + desc.InputLayout.NumElements = countof(inputElementDesc); + + if (!d3d_compile(simple_snow, sizeof(simple_snow), NULL, "VSMain", "vs_5_0", &vs_code)) + goto error; + if (!d3d_compile(simple_snow, sizeof(simple_snow), NULL, "PSMain", "ps_5_0", &ps_code)) + goto error; + + if (!d3d12_init_pipeline( + d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_3])) + goto error; + + Release(vs_code); + Release(ps_code); + vs_code = NULL; + ps_code = NULL; + + if (!d3d_compile(snow, sizeof(snow), NULL, "VSMain", "vs_5_0", &vs_code)) + goto error; + if (!d3d_compile(snow, sizeof(snow), NULL, "PSMain", "ps_5_0", &ps_code)) + goto error; + + if (!d3d12_init_pipeline( + d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_4])) + goto error; + + Release(vs_code); + Release(ps_code); + vs_code = NULL; + ps_code = NULL; + + if (!d3d_compile(bokeh, sizeof(bokeh), NULL, "VSMain", "vs_5_0", &vs_code)) + goto error; + if (!d3d_compile(bokeh, sizeof(bokeh), NULL, "PSMain", "ps_5_0", &ps_code)) + goto error; + + if (!d3d12_init_pipeline( + d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_5])) + goto error; + + Release(vs_code); + Release(ps_code); + vs_code = NULL; + ps_code = NULL; + + if (!d3d_compile(snowflake, sizeof(snowflake), NULL, "VSMain", "vs_5_0", &vs_code)) + goto error; + if (!d3d_compile(snowflake, sizeof(snowflake), NULL, "PSMain", "ps_5_0", &ps_code)) + goto error; + + if (!d3d12_init_pipeline( + d3d12->device, vs_code, ps_code, NULL, &desc, &d3d12->pipes[VIDEO_SHADER_MENU_6])) + goto error; + + Release(vs_code); + Release(ps_code); + vs_code = NULL; + ps_code = NULL; + } } { From b1a2d1991c1d26504f09d11a53774d3a1e9b53c2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 13:09:05 +0200 Subject: [PATCH 411/517] (d3d10.c) Cleanups --- gfx/drivers/d3d10.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 681defe247..5980391bbb 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -214,6 +214,7 @@ static void d3d10_set_filtering(void* data, unsigned index, bool smooth) static void d3d10_gfx_set_rotation(void* data, unsigned rotation) { math_matrix_4x4 rot; + void* mapped_ubo = NULL; d3d10_video_t* d3d10 = (d3d10_video_t*)data; if (!d3d10) @@ -222,7 +223,6 @@ static void d3d10_gfx_set_rotation(void* data, unsigned rotation) matrix_4x4_rotate_z(rot, rotation * (M_PI / 2.0f)); matrix_4x4_multiply(d3d10->mvp, rot, d3d10->ubo_values.mvp); - void* mapped_ubo; D3D10MapBuffer(d3d10->frame.ubo, D3D10_MAP_WRITE_DISCARD, 0, &mapped_ubo); *(math_matrix_4x4*)mapped_ubo = d3d10->mvp; D3D10UnmapBuffer(d3d10->frame.ubo); @@ -451,7 +451,8 @@ static bool d3d10_gfx_set_shader(void* data, if (!desc.ByteWidth) continue; - D3D10CreateBuffer(d3d10->device, &desc, NULL, &d3d10->pass[i].buffers[j]); + D3D10CreateBuffer(d3d10->device, &desc, + NULL, &d3d10->pass[i].buffers[j]); } } @@ -474,7 +475,8 @@ static bool d3d10_gfx_set_shader(void* data, d3d10_update_texture( d3d10->device, - image.width, image.height, 0, DXGI_FORMAT_R8G8B8A8_UNORM, image.pixels, + image.width, image.height, 0, + DXGI_FORMAT_R8G8B8A8_UNORM, image.pixels, &d3d10->luts[i]); image_texture_free(&image); @@ -561,7 +563,8 @@ static void d3d10_gfx_free(void* data) } static void* -d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** input_data) +d3d10_gfx_init(const video_info_t* video, + const input_driver_t** input, void** input_data) { unsigned i; MONITORINFOEX current_mon; @@ -584,11 +587,14 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i d3d10->vp.full_height = video->height; if (!d3d10->vp.full_width) - d3d10->vp.full_width = current_mon.rcMonitor.right - current_mon.rcMonitor.left; + d3d10->vp.full_width = + current_mon.rcMonitor.right - current_mon.rcMonitor.left; if (!d3d10->vp.full_height) - d3d10->vp.full_height = current_mon.rcMonitor.bottom - current_mon.rcMonitor.top; + d3d10->vp.full_height = + current_mon.rcMonitor.bottom - current_mon.rcMonitor.top; - if (!win32_set_video_mode(d3d10, d3d10->vp.full_width, d3d10->vp.full_height, video->fullscreen)) + if (!win32_set_video_mode(d3d10, + d3d10->vp.full_width, d3d10->vp.full_height, video->fullscreen)) { RARCH_ERR("[D3D10]: win32_set_video_mode failed.\n"); goto error; @@ -622,7 +628,8 @@ d3d10_gfx_init(const video_info_t* video, const input_driver_t** input, void** i #endif if (FAILED(D3D10CreateDeviceAndSwapChain( - NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, flags, D3D10_SDK_VERSION, &desc, + NULL, D3D10_DRIVER_TYPE_HARDWARE, + NULL, flags, D3D10_SDK_VERSION, &desc, (IDXGISwapChain**)&d3d10->swapChain, &d3d10->device))) goto error; } From 6f4fd8859ac6d7b2b96f5a6d1232bb427e49127c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 13:12:00 +0200 Subject: [PATCH 412/517] Add customizable swapchain images flag to VC EGL context --- gfx/drivers_context/vc_egl_ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/drivers_context/vc_egl_ctx.c b/gfx/drivers_context/vc_egl_ctx.c index e507056aed..56b8ba8933 100644 --- a/gfx/drivers_context/vc_egl_ctx.c +++ b/gfx/drivers_context/vc_egl_ctx.c @@ -695,7 +695,7 @@ static gfx_ctx_proc_t gfx_ctx_vc_get_proc_address(const char *symbol) static uint32_t gfx_ctx_vc_get_flags(void *data) { uint32_t flags = 0; - BIT32_SET(flags, GFX_CTX_FLAGS_NONE); + BIT32_SET(flags, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES); return flags; } From c4c8699ee3c98b865a0e9bcb0d97184c7f72c691 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 13:34:30 +0200 Subject: [PATCH 413/517] Only show max swapchain images if supported by video driver and/or context driver --- gfx/drivers/caca_gfx.c | 1 + gfx/drivers/ctr_gfx.c | 1 + gfx/drivers/d3d10.c | 1 + gfx/drivers/d3d11.c | 1 + gfx/drivers/d3d12.c | 1 + gfx/drivers/d3d8.c | 3 ++- gfx/drivers/d3d9.c | 1 + gfx/drivers/dispmanx_gfx.c | 1 + gfx/drivers/drm_gfx.c | 1 + gfx/drivers/exynos_gfx.c | 1 + gfx/drivers/gdi_gfx.c | 1 + gfx/drivers/gl.c | 1 + gfx/drivers/gx2_gfx.c | 4 ++-- gfx/drivers/gx_gfx.c | 1 + gfx/drivers/omap_gfx.c | 1 + gfx/drivers/psp1_gfx.c | 1 + gfx/drivers/sdl2_gfx.c | 1 + gfx/drivers/sdl_gfx.c | 1 + gfx/drivers/sunxi_gfx.c | 1 + gfx/drivers/switch_gfx.c | 1 + gfx/drivers/vga_gfx.c | 1 + gfx/drivers/vita2d_gfx.c | 1 + gfx/drivers/vulkan.c | 10 +++++++++ gfx/drivers/xshm_gfx.c | 1 + gfx/drivers/xvideo.c | 1 + gfx/video_driver.c | 12 +++++++--- gfx/video_driver.h | 3 +++ gfx/video_thread_wrapper.c | 9 ++++++++ menu/menu_setting.c | 46 ++++++++++++++++++++++++++------------ 29 files changed, 89 insertions(+), 20 deletions(-) diff --git a/gfx/drivers/caca_gfx.c b/gfx/drivers/caca_gfx.c index 1738a877e5..a9405a6be1 100644 --- a/gfx/drivers/caca_gfx.c +++ b/gfx/drivers/caca_gfx.c @@ -305,6 +305,7 @@ static void caca_set_osd_msg(void *data, } static const video_poke_interface_t caca_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/ctr_gfx.c b/gfx/drivers/ctr_gfx.c index bec7fc01e2..74a5c074d2 100644 --- a/gfx/drivers/ctr_gfx.c +++ b/gfx/drivers/ctr_gfx.c @@ -1133,6 +1133,7 @@ static void ctr_set_osd_msg(void *data, } static const video_poke_interface_t ctr_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ ctr_load_texture, diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 5980391bbb..1e09d00a2b 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -1584,6 +1584,7 @@ d3d10_get_hw_render_interface(void* data, const struct retro_hw_render_interface #endif static const video_poke_interface_t d3d10_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ d3d10_gfx_load_texture, diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 8a5a6f1f7d..f90b7bc532 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -1605,6 +1605,7 @@ d3d11_get_hw_render_interface(void* data, const struct retro_hw_render_interface } static const video_poke_interface_t d3d11_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ d3d11_gfx_load_texture, diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index a7b6532984..5474cea885 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -1751,6 +1751,7 @@ static void d3d12_gfx_unload_texture(void* data, uintptr_t handle) } static const video_poke_interface_t d3d12_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ d3d12_gfx_load_texture, diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index c614b97088..e0306cb6b2 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -1861,7 +1861,8 @@ static void d3d8_set_video_mode(void *data, } static const video_poke_interface_t d3d_poke_interface = { - NULL, /* set_coords */ + NULL, /* get_flags */ + NULL, /* set_coords */ d3d8_set_mvp, d3d8_load_texture, d3d8_unload_texture, diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index 2d1b4a0447..1aa709efb4 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -1887,6 +1887,7 @@ static void d3d9_set_video_mode(void *data, } static const video_poke_interface_t d3d9_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ d3d9_set_mvp, d3d9_load_texture, diff --git a/gfx/drivers/dispmanx_gfx.c b/gfx/drivers/dispmanx_gfx.c index 7d80b453be..386d2bcc38 100644 --- a/gfx/drivers/dispmanx_gfx.c +++ b/gfx/drivers/dispmanx_gfx.c @@ -631,6 +631,7 @@ static void dispmanx_set_aspect_ratio (void *data, unsigned aspect_ratio_idx) } static const video_poke_interface_t dispmanx_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/drm_gfx.c b/gfx/drivers/drm_gfx.c index ba069adcb5..cd43998614 100644 --- a/gfx/drivers/drm_gfx.c +++ b/gfx/drivers/drm_gfx.c @@ -966,6 +966,7 @@ static void drm_set_aspect_ratio (void *data, unsigned aspect_ratio_idx) } static const video_poke_interface_t drm_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/exynos_gfx.c b/gfx/drivers/exynos_gfx.c index 659e87bba5..2ce72de395 100644 --- a/gfx/drivers/exynos_gfx.c +++ b/gfx/drivers/exynos_gfx.c @@ -1490,6 +1490,7 @@ static void exynos_show_mouse(void *data, bool state) } static const video_poke_interface_t exynos_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/gdi_gfx.c b/gfx/drivers/gdi_gfx.c index 2d9f96d2e3..dae5a1b091 100644 --- a/gfx/drivers/gdi_gfx.c +++ b/gfx/drivers/gdi_gfx.c @@ -533,6 +533,7 @@ static void gdi_set_video_mode(void *data, unsigned width, unsigned height, } static const video_poke_interface_t gdi_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 710866283e..1add4ff6a8 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2601,6 +2601,7 @@ static void gl_set_mvp(void *data, void *shader_data, } static const video_poke_interface_t gl_poke_interface = { + NULL, /* get_flags */ gl_set_coords, gl_set_mvp, gl_load_texture, diff --git a/gfx/drivers/gx2_gfx.c b/gfx/drivers/gx2_gfx.c index 853e1bdb56..f8dc6e1384 100644 --- a/gfx/drivers/gx2_gfx.c +++ b/gfx/drivers/gx2_gfx.c @@ -1711,8 +1711,8 @@ static void wiiu_gfx_set_osd_msg(void *data, } -static const video_poke_interface_t wiiu_poke_interface = -{ +static const video_poke_interface_t wiiu_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ wiiu_gfx_load_texture, diff --git a/gfx/drivers/gx_gfx.c b/gfx/drivers/gx_gfx.c index e07294f0ed..3118fa944b 100644 --- a/gfx/drivers/gx_gfx.c +++ b/gfx/drivers/gx_gfx.c @@ -1266,6 +1266,7 @@ static void gx_get_video_output_next(void *data) } static const video_poke_interface_t gx_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/omap_gfx.c b/gfx/drivers/omap_gfx.c index fc7bc1fd21..5b4fcd0d70 100644 --- a/gfx/drivers/omap_gfx.c +++ b/gfx/drivers/omap_gfx.c @@ -1140,6 +1140,7 @@ static float omap_get_refresh_rate(void *data) } static const video_poke_interface_t omap_gfx_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/psp1_gfx.c b/gfx/drivers/psp1_gfx.c index 7a2e49b77a..57b38fe727 100644 --- a/gfx/drivers/psp1_gfx.c +++ b/gfx/drivers/psp1_gfx.c @@ -831,6 +831,7 @@ static void psp_viewport_info(void *data, struct video_viewport *vp) } static const video_poke_interface_t psp_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/sdl2_gfx.c b/gfx/drivers/sdl2_gfx.c index bbed0bbc68..75f3105e5e 100644 --- a/gfx/drivers/sdl2_gfx.c +++ b/gfx/drivers/sdl2_gfx.c @@ -721,6 +721,7 @@ static void sdl2_grab_mouse_toggle(void *data) } static video_poke_interface_t sdl2_video_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/sdl_gfx.c b/gfx/drivers/sdl_gfx.c index 5f5978ae6e..83ad492680 100644 --- a/gfx/drivers/sdl_gfx.c +++ b/gfx/drivers/sdl_gfx.c @@ -517,6 +517,7 @@ static void sdl_grab_mouse_toggle(void *data) } static const video_poke_interface_t sdl_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/sunxi_gfx.c b/gfx/drivers/sunxi_gfx.c index d2336eaaf8..baf909520a 100644 --- a/gfx/drivers/sunxi_gfx.c +++ b/gfx/drivers/sunxi_gfx.c @@ -943,6 +943,7 @@ static float sunxi_get_refresh_rate (void *data) } static const video_poke_interface_t sunxi_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/switch_gfx.c b/gfx/drivers/switch_gfx.c index 19d646279f..529f74e04a 100644 --- a/gfx/drivers/switch_gfx.c +++ b/gfx/drivers/switch_gfx.c @@ -387,6 +387,7 @@ static void switch_set_texture_enable(void *data, bool enable, bool full_screen) } static const video_poke_interface_t switch_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, /* load_texture */ diff --git a/gfx/drivers/vga_gfx.c b/gfx/drivers/vga_gfx.c index 6ff8e98d4d..d1a0dad125 100644 --- a/gfx/drivers/vga_gfx.c +++ b/gfx/drivers/vga_gfx.c @@ -390,6 +390,7 @@ static void vga_set_osd_msg(void *data, } static const video_poke_interface_t vga_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/vita2d_gfx.c b/gfx/drivers/vita2d_gfx.c index 179fdbaaf9..7f49756c5c 100644 --- a/gfx/drivers/vita2d_gfx.c +++ b/gfx/drivers/vita2d_gfx.c @@ -790,6 +790,7 @@ static bool vita_get_current_sw_framebuffer(void *data, } static const video_poke_interface_t vita_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ vita_load_texture, diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index c6c2aac6d6..731f347b5c 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -2302,7 +2302,17 @@ static float vulkan_get_refresh_rate(void *data) return 0.0f; } +static uint32_t vulkan_get_flags(void *data) +{ + uint32_t flags = 0; + + BIT32_SET(flags, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES); + + return flags; +} + static const video_poke_interface_t vulkan_poke_interface = { + vulkan_get_flags, NULL, /* set_coords */ NULL, /* set_mvp */ vulkan_load_texture, diff --git a/gfx/drivers/xshm_gfx.c b/gfx/drivers/xshm_gfx.c index 7f6ce812f8..a953f2d052 100644 --- a/gfx/drivers/xshm_gfx.c +++ b/gfx/drivers/xshm_gfx.c @@ -204,6 +204,7 @@ static void xshm_grab_mouse_toggle(void *data) } static video_poke_interface_t xshm_video_poke_interface = { + NULL, /* get_flags */ NULL, /* set_coords */ NULL, /* set_mvp */ NULL, diff --git a/gfx/drivers/xvideo.c b/gfx/drivers/xvideo.c index bb6f1dc6e9..d81bc83d61 100644 --- a/gfx/drivers/xvideo.c +++ b/gfx/drivers/xvideo.c @@ -934,6 +934,7 @@ static bool xv_read_viewport(void *data, uint8_t *buffer, bool is_idle) } static video_poke_interface_t xv_video_poke_interface = { + NULL, /* get_flags */ NULL, NULL, NULL, diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 7e2b388e61..3c041da9ff 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -3280,11 +3280,17 @@ void video_context_driver_set_data(void *data) video_context_data = data; } +bool video_driver_get_flags(gfx_ctx_flags_t *flags) +{ + if (!flags || !video_driver_poke || !video_driver_poke->get_flags) + return false; + flags->flags = video_driver_poke->get_flags(video_driver_data); + return true; +} + bool video_context_driver_get_flags(gfx_ctx_flags_t *flags) { - if (!flags) - return false; - if (!current_video_context.get_flags) + if (!flags || !current_video_context.get_flags) return false; if (deferred_video_context_driver_set_flags) diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 0d2336d003..baa8fbe9de 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -694,6 +694,7 @@ struct aspect_ratio_elem typedef struct video_poke_interface { + uint32_t (*get_flags)(void *data); void (*set_coords)(void *handle_data, void *shader_data, const struct video_coords *coords); void (*set_mvp)(void *data, void *shader_data, @@ -1285,6 +1286,8 @@ bool video_context_driver_show_mouse(bool *bool_data); void video_context_driver_set_data(void *data); +bool video_driver_get_flags(gfx_ctx_flags_t *flags); + bool video_context_driver_get_flags(gfx_ctx_flags_t *flags); bool video_context_driver_set_flags(gfx_ctx_flags_t *flags); diff --git a/gfx/video_thread_wrapper.c b/gfx/video_thread_wrapper.c index e6216a6d8c..147ec5782b 100644 --- a/gfx/video_thread_wrapper.c +++ b/gfx/video_thread_wrapper.c @@ -1252,7 +1252,16 @@ static struct video_shader *thread_get_current_shader(void *data) return thr->poke->get_current_shader(thr->driver_data); } +static uint32_t thread_get_flags(void *data) +{ + thread_video_t *thr = (thread_video_t*)data; + if (!thr || !thr->poke || !thr->poke->get_flags) + return 0; + return thr->poke->get_flags(thr->driver_data); +} + static const video_poke_interface_t thread_poke = { + thread_get_flags, NULL, /* set_coords */ NULL, /* set_mvp */ thread_load_texture, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 219bed435d..5ffa0f67b1 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -3579,20 +3579,38 @@ static bool setting_append_list( settings_data_list_current_add_flags(list, list_info, SD_FLAG_CMD_APPLY_AUTO); settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); - CONFIG_UINT( - list, list_info, - &settings->uints.video_max_swapchain_images, - MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES, - MENU_ENUM_LABEL_VALUE_VIDEO_MAX_SWAPCHAIN_IMAGES, - max_swapchain_images, - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler); - menu_settings_list_current_add_range(list, list_info, 1, 4, 1, true, true); - settings_data_list_current_add_flags(list, list_info, SD_FLAG_CMD_APPLY_AUTO); - settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); + { + gfx_ctx_flags_t flags; + bool customizable_swapchain_set = false; + + if (video_driver_get_flags(&flags)) + if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES)) + customizable_swapchain_set = true; + + flags.flags = 0; + + if (video_context_driver_get_flags(&flags)) + if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES)) + customizable_swapchain_set = true; + + if (customizable_swapchain_set) + { + CONFIG_UINT( + list, list_info, + &settings->uints.video_max_swapchain_images, + MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES, + MENU_ENUM_LABEL_VALUE_VIDEO_MAX_SWAPCHAIN_IMAGES, + max_swapchain_images, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + menu_settings_list_current_add_range(list, list_info, 1, 4, 1, true, true); + settings_data_list_current_add_flags(list, list_info, SD_FLAG_CMD_APPLY_AUTO); + settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); + } + } if (string_is_equal(settings->arrays.video_driver, "gl")) { From f1fa1d48797391103419c14d50a388cc4a965a41 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 13:42:09 +0200 Subject: [PATCH 414/517] Previously, GPU Hard Sync options would only show up if the video driver was explicitly set to the GL driver; now, it can be dynamically shown for more video drivers if there are going to be more drivers supporting this feature other than GL --- gfx/drivers/gl.c | 12 +++++++- gfx/video_driver.h | 3 +- menu/menu_setting.c | 74 ++++++++++++++++++++++++++------------------- 3 files changed, 56 insertions(+), 33 deletions(-) diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 1add4ff6a8..8b7681c029 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2600,8 +2600,18 @@ static void gl_set_mvp(void *data, void *shader_data, shader_data, mat_data); } +static uint32_t gl_get_flags(void *data) +{ + uint32_t flags = 0; + + if (gl_check_capability(GL_CAPS_SYNC)) + BIT32_SET(flags, GFX_CTX_FLAGS_HARD_SYNC); + + return flags; +} + static const video_poke_interface_t gl_poke_interface = { - NULL, /* get_flags */ + gl_get_flags, gl_set_coords, gl_set_mvp, gl_load_texture, diff --git a/gfx/video_driver.h b/gfx/video_driver.h index baa8fbe9de..b05e4790f1 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -113,7 +113,8 @@ enum display_flags GFX_CTX_FLAGS_NONE = 0, GFX_CTX_FLAGS_GL_CORE_CONTEXT, GFX_CTX_FLAGS_MULTISAMPLING, - GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES + GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES, + GFX_CTX_FLAGS_HARD_SYNC }; enum shader_uniform_type diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 5ffa0f67b1..7bec767190 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -3608,42 +3608,54 @@ static bool setting_append_list( general_read_handler); menu_settings_list_current_add_range(list, list_info, 1, 4, 1, true, true); settings_data_list_current_add_flags(list, list_info, SD_FLAG_CMD_APPLY_AUTO); - settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); } } - if (string_is_equal(settings->arrays.video_driver, "gl")) { - CONFIG_BOOL( - list, list_info, - &settings->bools.video_hard_sync, - MENU_ENUM_LABEL_VIDEO_HARD_SYNC, - MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC, - hard_sync, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler, - SD_FLAG_NONE - ); - settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); + gfx_ctx_flags_t flags; + bool hard_sync_supported = false; - CONFIG_UINT( - list, list_info, - &settings->uints.video_hard_sync_frames, - MENU_ENUM_LABEL_VIDEO_HARD_SYNC_FRAMES, - MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC_FRAMES, - hard_sync_frames, - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler); - menu_settings_list_current_add_range(list, list_info, 0, 3, 1, true, true); - settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); + if (video_driver_get_flags(&flags)) + if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_HARD_SYNC)) + hard_sync_supported = true; + + flags.flags = 0; + + if (video_context_driver_get_flags(&flags)) + if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_HARD_SYNC)) + hard_sync_supported = true; + + if (hard_sync_supported) + { + CONFIG_BOOL( + list, list_info, + &settings->bools.video_hard_sync, + MENU_ENUM_LABEL_VIDEO_HARD_SYNC, + MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC, + hard_sync, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + + CONFIG_UINT( + list, list_info, + &settings->uints.video_hard_sync_frames, + MENU_ENUM_LABEL_VIDEO_HARD_SYNC_FRAMES, + MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC_FRAMES, + hard_sync_frames, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + menu_settings_list_current_add_range(list, list_info, 0, 3, 1, true, true); + } } CONFIG_UINT( From ee621ba4768a9357bc90c7f546d0a3894ee1e6a9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 14:07:10 +0200 Subject: [PATCH 415/517] Silence Coverity warning --- configuration.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configuration.c b/configuration.c index b98fd08d47..a03257b708 100644 --- a/configuration.c +++ b/configuration.c @@ -3767,8 +3767,7 @@ bool config_save_autoconf_profile(const char *path, unsigned user) error: free(buf); free(autoconf_file); - if (path_new) - free(path_new); + free(path_new); return false; } From e54cb1b121410862761b04828617875a54f6b021 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 14:15:21 +0200 Subject: [PATCH 416/517] Only show black frame insertion for the video drivers/context drivers that support it --- gfx/drivers/d3d8.c | 11 +++++++++- gfx/drivers/d3d9.c | 11 +++++++++- gfx/drivers/gl.c | 2 ++ gfx/drivers/vulkan.c | 1 + gfx/video_driver.h | 3 ++- menu/menu_setting.c | 50 ++++++++++++++++++++++++++++++-------------- 6 files changed, 59 insertions(+), 19 deletions(-) diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index e0306cb6b2..94fc4f7269 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -1860,8 +1860,17 @@ static void d3d8_set_video_mode(void *data, #endif } +static uint32_t d3d8_get_flags(void *data) +{ + uint32_t flags = 0; + + BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); + + return flags; +} + static const video_poke_interface_t d3d_poke_interface = { - NULL, /* get_flags */ + d3d8_get_flags, NULL, /* set_coords */ d3d8_set_mvp, d3d8_load_texture, diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index 1aa709efb4..bb5593314c 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -1886,8 +1886,17 @@ static void d3d9_set_video_mode(void *data, #endif } +static uint32_t d3d9_get_flags(void *data) +{ + uint32_t flags = 0; + + BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); + + return flags; +} + static const video_poke_interface_t d3d9_poke_interface = { - NULL, /* get_flags */ + d3d9_get_flags, NULL, /* set_coords */ d3d9_set_mvp, d3d9_load_texture, diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 8b7681c029..0cf00d7a7a 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2607,6 +2607,8 @@ static uint32_t gl_get_flags(void *data) if (gl_check_capability(GL_CAPS_SYNC)) BIT32_SET(flags, GFX_CTX_FLAGS_HARD_SYNC); + BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); + return flags; } diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 731f347b5c..a4f2c55267 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -2307,6 +2307,7 @@ static uint32_t vulkan_get_flags(void *data) uint32_t flags = 0; BIT32_SET(flags, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES); + BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); return flags; } diff --git a/gfx/video_driver.h b/gfx/video_driver.h index b05e4790f1..90a93e6794 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -114,7 +114,8 @@ enum display_flags GFX_CTX_FLAGS_GL_CORE_CONTEXT, GFX_CTX_FLAGS_MULTISAMPLING, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES, - GFX_CTX_FLAGS_HARD_SYNC + GFX_CTX_FLAGS_HARD_SYNC, + GFX_CTX_FLAGS_BLACK_FRAME_INSERTION }; enum shader_uniform_type diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 7bec767190..a17aaeea15 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -3673,22 +3673,40 @@ static bool setting_append_list( settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); #if !defined(RARCH_MOBILE) - CONFIG_BOOL( - list, list_info, - &settings->bools.video_black_frame_insertion, - MENU_ENUM_LABEL_VIDEO_BLACK_FRAME_INSERTION, - MENU_ENUM_LABEL_VALUE_VIDEO_BLACK_FRAME_INSERTION, - black_frame_insertion, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler, - SD_FLAG_NONE - ); - settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); + { + gfx_ctx_flags_t flags; + bool black_frame_insertion_supported = false; + + if (video_driver_get_flags(&flags)) + if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION)) + black_frame_insertion_supported = true; + + flags.flags = 0; + + if (video_context_driver_get_flags(&flags)) + if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION)) + black_frame_insertion_supported = true; + + if (black_frame_insertion_supported) + { + CONFIG_BOOL( + list, list_info, + &settings->bools.video_black_frame_insertion, + MENU_ENUM_LABEL_VIDEO_BLACK_FRAME_INSERTION, + MENU_ENUM_LABEL_VALUE_VIDEO_BLACK_FRAME_INSERTION, + black_frame_insertion, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); + } + } #endif END_SUB_GROUP(list, list_info, parent_group); START_SUB_GROUP( From 871f04e19a61629f4525ee4a83639cb346fa6bfa Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 14:56:02 +0200 Subject: [PATCH 417/517] (slang) Cleanups --- gfx/drivers_shader/slang_preprocess.cpp | 35 ++++---- gfx/drivers_shader/slang_process.cpp | 113 +++++++++++++++--------- gfx/drivers_shader/slang_reflection.cpp | 24 ++--- 3 files changed, 104 insertions(+), 68 deletions(-) diff --git a/gfx/drivers_shader/slang_preprocess.cpp b/gfx/drivers_shader/slang_preprocess.cpp index ec4daa666e..0119be2346 100644 --- a/gfx/drivers_shader/slang_preprocess.cpp +++ b/gfx/drivers_shader/slang_preprocess.cpp @@ -18,8 +18,10 @@ #include #include #include + +#include + #include "../../verbosity.h" -#include "../../libretro-common/include/compat/strl.h" using namespace std; @@ -28,28 +30,32 @@ bool slang_preprocess_parse_parameters(glslang_meta& meta, { unsigned old_num_parameters = shader->num_parameters; - // Assumes num_parameters is initialized to something sane. + /* Assumes num_parameters is + * initialized to something sane. */ for (auto ¶m : meta.parameters) { bool mismatch_dup = false; - bool dup = false; - - auto itr = find_if(shader->parameters, shader->parameters + shader->num_parameters, - [&](const video_shader_parameter &parsed_param) { + bool dup = false; + auto itr = find_if(shader->parameters, + shader->parameters + shader->num_parameters, + [&](const video_shader_parameter &parsed_param) + { return param.id == parsed_param.id; }); if (itr != shader->parameters + shader->num_parameters) { dup = true; - // Allow duplicate #pragma parameter, but only if they are exactly the same. + /* Allow duplicate #pragma parameter, but only + * if they are exactly the same. */ if (param.desc != itr->desc || - param.initial != itr->initial || - param.minimum != itr->minimum || - param.maximum != itr->maximum || - param.step != itr->step) + param.initial != itr->initial || + param.minimum != itr->minimum || + param.maximum != itr->maximum || + param.step != itr->step) { - RARCH_ERR("[Vulkan]: Duplicate parameters found for \"%s\", but arguments do not match.\n", + RARCH_ERR("[Vulkan]: Duplicate parameters" + " found for \"%s\", but arguments do not match.\n", itr->id); mismatch_dup = true; } @@ -70,7 +76,7 @@ bool slang_preprocess_parse_parameters(glslang_meta& meta, p.initial = param.initial; p.minimum = param.minimum; p.maximum = param.maximum; - p.step = param.step; + p.step = param.step; p.current = param.initial; } @@ -82,12 +88,11 @@ bool slang_preprocess_parse_parameters(const char *shader_path, { glslang_meta meta; vector lines; + if (!glslang_read_shader_file(shader_path, &lines, true)) return false; - if (!glslang_parse_meta(lines, &meta)) return false; - return slang_preprocess_parse_parameters(meta, shader); } diff --git a/gfx/drivers_shader/slang_process.cpp b/gfx/drivers_shader/slang_process.cpp index f4bf258813..a6cc60f675 100644 --- a/gfx/drivers_shader/slang_process.cpp +++ b/gfx/drivers_shader/slang_process.cpp @@ -17,7 +17,8 @@ using namespace spirv_cross; using namespace std; template -static bool set_unique_map(unordered_map& m, const string& name, const P& p) +static bool set_unique_map(unordered_map& m, + const string& name, const P& p) { auto itr = m.find(name); if (itr != end(m)) @@ -31,7 +32,8 @@ static bool set_unique_map(unordered_map& m, const string& name, cons } template -static string get_semantic_name(const unordered_map* map, S semantic, unsigned index) +static string get_semantic_name(const unordered_map* map, + S semantic, unsigned index) { for (const pair& m : *map) { @@ -42,7 +44,8 @@ static string get_semantic_name(const unordered_map* map, S semantic, } static string -get_semantic_name(slang_reflection& reflection, slang_semantic semantic, unsigned index) +get_semantic_name(slang_reflection& reflection, + slang_semantic semantic, unsigned index) { static const char* names[] = { "MVP", @@ -57,7 +60,8 @@ get_semantic_name(slang_reflection& reflection, slang_semantic semantic, unsigne } static string -get_semantic_name(slang_reflection& reflection, slang_texture_semantic semantic, unsigned index) +get_semantic_name(slang_reflection& reflection, + slang_texture_semantic semantic, unsigned index) { static const char* names[] = { "Original", "Source", "OriginalHistory", "PassOutput", "PassFeedback", @@ -71,7 +75,8 @@ get_semantic_name(slang_reflection& reflection, slang_texture_semantic semantic, } static string get_size_semantic_name( - slang_reflection& reflection, slang_texture_semantic semantic, unsigned index) + slang_reflection& reflection, + slang_texture_semantic semantic, unsigned index) { static const char* names[] = { "OriginalSize", "SourceSize", "OriginalHistorySize", "PassOutputSize", "PassFeedbackSize", @@ -96,6 +101,8 @@ static bool slang_process_reflection( { int semantic; unsigned i; + vector textures; + vector uniforms[SLANG_CBUFFER_MAX]; unordered_map texture_semantic_map; unordered_map texture_semantic_uniform_map; @@ -108,22 +115,26 @@ static bool slang_process_reflection( if (!set_unique_map( texture_semantic_map, name, - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, i })) + slang_texture_semantic_map{ + SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, i })) return false; if (!set_unique_map( texture_semantic_uniform_map, name + "Size", - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, i })) + slang_texture_semantic_map{ + SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, i })) return false; if (!set_unique_map( texture_semantic_map, name + "Feedback", - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, i })) + slang_texture_semantic_map{ + SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, i })) return false; if (!set_unique_map( texture_semantic_uniform_map, name + "FeedbackSize", - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, i })) + slang_texture_semantic_map{ + SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, i })) return false; } @@ -131,12 +142,15 @@ static bool slang_process_reflection( { if (!set_unique_map( texture_semantic_map, shader_info->lut[i].id, - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_USER, i })) + slang_texture_semantic_map{ + SLANG_TEXTURE_SEMANTIC_USER, i })) return false; if (!set_unique_map( - texture_semantic_uniform_map, string(shader_info->lut[i].id) + "Size", - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_USER, i })) + texture_semantic_uniform_map, + string(shader_info->lut[i].id) + "Size", + slang_texture_semantic_map{ + SLANG_TEXTURE_SEMANTIC_USER, i })) return false; } @@ -156,9 +170,11 @@ static bool slang_process_reflection( sl_reflection.texture_semantic_uniform_map = &texture_semantic_uniform_map; sl_reflection.semantic_map = &uniform_semantic_map; - if (!slang_reflect(*vs_compiler, *ps_compiler, vs_resources, ps_resources, &sl_reflection)) + if (!slang_reflect(*vs_compiler, *ps_compiler, + vs_resources, ps_resources, &sl_reflection)) { - RARCH_ERR("[slang]: Failed to reflect SPIR-V. Resource usage is inconsistent with " + RARCH_ERR("[slang]: Failed to reflect SPIR-V." + " Resource usage is inconsistent with " "expectations.\n"); return false; } @@ -170,18 +186,17 @@ static bool slang_process_reflection( out->cbuffers[SLANG_CBUFFER_PC].binding = sl_reflection.ubo_binding ? 0 : 1; out->cbuffers[SLANG_CBUFFER_PC].size = (sl_reflection.push_constant_size + 0xF) & ~0xF; - vector uniforms[SLANG_CBUFFER_MAX]; - vector textures; - for (semantic = 0; semantic < SLANG_NUM_BASE_SEMANTICS; semantic++) { slang_semantic_meta& src = sl_reflection.semantics[semantic]; if (src.push_constant || src.uniform) { uniform_sem_t uniform = { map->uniforms[semantic], - src.num_components * (unsigned)sizeof(float) }; + src.num_components + * (unsigned)sizeof(float) }; + string uniform_id = get_semantic_name( + sl_reflection, (slang_semantic)semantic, 0); - string uniform_id = get_semantic_name(sl_reflection, (slang_semantic)semantic, 0); strncpy(uniform.id, uniform_id.c_str(), sizeof(uniform.id)); if (src.push_constant) @@ -203,9 +218,11 @@ static bool slang_process_reflection( if (src.push_constant || src.uniform) { - uniform_sem_t uniform = { &shader_info->parameters[i].current, sizeof(float) }; + uniform_sem_t uniform = { + &shader_info->parameters[i].current, sizeof(float) }; - string uniform_id = get_semantic_name(sl_reflection, SLANG_SEMANTIC_FLOAT_PARAMETER, i); + string uniform_id = get_semantic_name( + sl_reflection, SLANG_SEMANTIC_FLOAT_PARAMETER, i); strncpy(uniform.id, uniform_id.c_str(), sizeof(uniform.id)); if (src.push_constant) @@ -225,9 +242,11 @@ static bool slang_process_reflection( { unsigned index; - for (index = 0; index < sl_reflection.semantic_textures[semantic].size(); index++) + for (index = 0; index < + sl_reflection.semantic_textures[semantic].size(); index++) { - slang_texture_semantic_meta& src = sl_reflection.semantic_textures[semantic][index]; + slang_texture_semantic_meta& src = + sl_reflection.semantic_textures[semantic][index]; if (src.stage_mask) { @@ -237,17 +256,18 @@ static bool slang_process_reflection( if (semantic == SLANG_TEXTURE_SEMANTIC_USER) { - texture.wrap = shader_info->lut[index].wrap; - texture.filter = shader_info->lut[index].filter; + texture.wrap = shader_info->lut[index].wrap; + texture.filter = shader_info->lut[index].filter; } else { - texture.wrap = shader_info->pass[pass_number].wrap; - texture.filter = shader_info->pass[pass_number].filter; + texture.wrap = shader_info->pass[pass_number].wrap; + texture.filter = shader_info->pass[pass_number].filter; } texture.stage_mask = src.stage_mask; texture.binding = src.binding; - string id = get_semantic_name(sl_reflection, (slang_texture_semantic)semantic, index); + string id = get_semantic_name( + sl_reflection, (slang_texture_semantic)semantic, index); strncpy(texture.id, id.c_str(), sizeof(texture.id)); @@ -264,12 +284,15 @@ static bool slang_process_reflection( if (src.push_constant || src.uniform) { uniform_sem_t uniform = { - (void*)((uintptr_t)map->textures[semantic].size + index * map->textures[semantic].size_stride), + (void*)((uintptr_t)map->textures[semantic].size + + index * map->textures[semantic].size_stride), 4 * sizeof(float) }; string uniform_id = - get_size_semantic_name(sl_reflection, (slang_texture_semantic)semantic, index); + get_size_semantic_name( + sl_reflection, + (slang_texture_semantic)semantic, index); strncpy(uniform.id, uniform_id.c_str(), sizeof(uniform.id)); @@ -290,8 +313,10 @@ static bool slang_process_reflection( out->texture_count = textures.size(); textures.push_back({ NULL }); - out->textures = (texture_sem_t*)malloc(textures.size() * sizeof(*textures.data())); - memcpy(out->textures, textures.data(), textures.size() * sizeof(*textures.data())); + out->textures = (texture_sem_t*) + malloc(textures.size() * sizeof(*textures.data())); + memcpy(out->textures, textures.data(), + textures.size() * sizeof(*textures.data())); for (i = 0; i < SLANG_CBUFFER_MAX; i++) { @@ -302,7 +327,9 @@ static bool slang_process_reflection( uniforms[i].push_back({ NULL }); out->cbuffers[i].uniforms = - (uniform_sem_t*)malloc(uniforms[i].size() * sizeof(*uniforms[i].data())); + (uniform_sem_t*) + malloc(uniforms[i].size() * sizeof(*uniforms[i].data())); + memcpy( out->cbuffers[i].uniforms, uniforms[i].data(), uniforms[i].size() * sizeof(*uniforms[i].data())); @@ -319,10 +346,10 @@ bool slang_process( const semantics_map_t* semantics_map, pass_semantics_t* out) { + glslang_output output; Compiler* vs_compiler = NULL; Compiler* ps_compiler = NULL; video_shader_pass& pass = shader_info->pass[pass_number]; - glslang_output output; if (!glslang_compile_shader(pass.source.path, &output)) return false; @@ -370,9 +397,11 @@ bool slang_process( ps_resources = ps_compiler->get_shader_resources(); if (!vs_resources.uniform_buffers.empty()) - vs_compiler->set_decoration(vs_resources.uniform_buffers[0].id, spv::DecorationBinding, 0); + vs_compiler->set_decoration( + vs_resources.uniform_buffers[0].id, spv::DecorationBinding, 0); if (!ps_resources.uniform_buffers.empty()) - ps_compiler->set_decoration(ps_resources.uniform_buffers[0].id, spv::DecorationBinding, 0); + ps_compiler->set_decoration( + ps_resources.uniform_buffers[0].id, spv::DecorationBinding, 0); if (!vs_resources.push_constant_buffers.empty()) vs_compiler->set_decoration( @@ -390,12 +419,12 @@ bool slang_process( vs->set_options(options); ps->set_options(options); - #if 0 +#if 0 CompilerGLSL::Options glsl_options; glsl_options.vertex.flip_vert_y = true; ((CompilerGLSL*)vs)->set_options(glsl_options); ((CompilerGLSL*)ps)->set_options(glsl_options); - #endif +#endif /* not exactly a vertex attribute but this remaps * float2 FragCoord :TEXCOORD# to float4 FragCoord : SV_POSITION */ @@ -404,9 +433,7 @@ bool slang_process( VariableTypeRemapCallback ps_var_remap_cb = [&](const SPIRType& type, const std::string& var_name, std::string& name_of_type) { if (var_name == "FragCoord") - { name_of_type = "float4"; - } }; for (Resource& resource : ps_resources.stage_inputs) { @@ -440,11 +467,13 @@ bool slang_process( pass.source.string.fragment = strdup(ps_code.c_str()); if (!slang_process_reflection( - vs_compiler, ps_compiler, vs_resources, ps_resources, shader_info, pass_number, + vs_compiler, ps_compiler, + vs_resources, ps_resources, shader_info, pass_number, semantics_map, out)) goto error; - } catch (const std::exception& e) + } + catch (const std::exception& e) { RARCH_ERR("[slang]: SPIRV-Cross threw exception: %s.\n", e.what()); goto error; diff --git a/gfx/drivers_shader/slang_reflection.cpp b/gfx/drivers_shader/slang_reflection.cpp index bd951f1e4b..6128e57661 100644 --- a/gfx/drivers_shader/slang_reflection.cpp +++ b/gfx/drivers_shader/slang_reflection.cpp @@ -83,12 +83,13 @@ static slang_texture_semantic slang_name_to_texture_semantic_array(const string unsigned i = 0; while (*names) { - auto n = *names; + auto n = *names; auto semantic = static_cast(i); + if (slang_texture_semantic_is_array(semantic)) { size_t baselen = strlen(n); - int cmp = strncmp(n, name.c_str(), baselen); + int cmp = strncmp(n, name.c_str(), baselen); if (cmp == 0) { @@ -190,7 +191,7 @@ static bool set_ubo_texture_offset(slang_reflection *reflection, } } - active = true; + active = true; active_offset = offset; return true; } @@ -279,7 +280,7 @@ static bool validate_type_for_semantic(const SPIRType &type, slang_semantic sem) return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 4; /* uint */ case SLANG_SEMANTIC_FRAME_COUNT: - return type.basetype == SPIRType::UInt && type.vecsize == 1 && type.columns == 1; + return type.basetype == SPIRType::UInt && type.vecsize == 1 && type.columns == 1; /* float */ case SLANG_SEMANTIC_FLOAT_PARAMETER: return type.basetype == SPIRType::Float && type.vecsize == 1 && type.columns == 1; @@ -303,13 +304,13 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r auto ranges = compiler.get_active_buffer_ranges(resource.id); for (auto &range : ranges) { - auto &name = compiler.get_member_name(resource.base_type_id, range.index); - auto &type = compiler.get_type(compiler.get_type(resource.base_type_id).member_types[range.index]); + auto &name = compiler.get_member_name(resource.base_type_id, range.index); + auto &type = compiler.get_type(compiler.get_type(resource.base_type_id).member_types[range.index]); - unsigned sem_index = 0; + unsigned sem_index = 0; unsigned tex_sem_index = 0; - auto sem = slang_uniform_name_to_semantic(*reflection->semantic_map, name, &sem_index); - auto tex_sem = slang_uniform_name_to_texture_semantic(*reflection->texture_semantic_uniform_map, + auto sem = slang_uniform_name_to_semantic(*reflection->semantic_map, name, &sem_index); + auto tex_sem = slang_uniform_name_to_texture_semantic(*reflection->texture_semantic_uniform_map, name, &tex_sem_index); if (tex_sem == SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT && tex_sem_index >= reflection->pass_number) @@ -658,14 +659,15 @@ bool slang_reflect_spirv(const std::vector &vertex, { Compiler vertex_compiler(vertex); Compiler fragment_compiler(fragment); - auto vertex_resources = vertex_compiler.get_shader_resources(); + auto vertex_resources = vertex_compiler.get_shader_resources(); auto fragment_resources = fragment_compiler.get_shader_resources(); if (!slang_reflect(vertex_compiler, fragment_compiler, vertex_resources, fragment_resources, reflection)) { - RARCH_ERR("[slang]: Failed to reflect SPIR-V. Resource usage is inconsistent with expectations.\n"); + RARCH_ERR("[slang]: Failed to reflect SPIR-V." + " Resource usage is inconsistent with expectations.\n"); return false; } From 45cea92ba220f5c735820b5000f71365e63cefad Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 14:57:37 +0200 Subject: [PATCH 418/517] Remove null_renderchain --- Makefile.common | 3 - gfx/drivers/d3d9.c | 2 - gfx/drivers_renderchain/null_renderchain.c | 135 --------------------- griffin/griffin.c | 1 - 4 files changed, 141 deletions(-) delete mode 100644 gfx/drivers_renderchain/null_renderchain.c diff --git a/Makefile.common b/Makefile.common index 81f850788e..dc96774b20 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1035,9 +1035,6 @@ ifeq ($(HAVE_PLAIN_DRM), 1) LIBS += -ldrm endif -OBJ += \ - gfx/drivers_renderchain/null_renderchain.o - ifeq ($(HAVE_GL_CONTEXT), 1) DEFINES += -DHAVE_OPENGL -DHAVE_GLSL OBJ += gfx/drivers/gl.o \ diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index bb5593314c..1f66f5b1d7 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -138,7 +138,6 @@ static bool d3d9_init_imports(d3d_video_t *d3d) extern d3d_renderchain_driver_t cg_d3d9_renderchain; extern d3d_renderchain_driver_t hlsl_d3d9_renderchain; -extern d3d_renderchain_driver_t null_d3d_renderchain; static bool renderchain_d3d_init_first( enum gfx_ctx_api api, @@ -157,7 +156,6 @@ static bool renderchain_d3d_init_first( #if defined(_WIN32) && defined(HAVE_HLSL) &hlsl_d3d9_renderchain, #endif - &null_d3d_renderchain, NULL }; unsigned i; diff --git a/gfx/drivers_renderchain/null_renderchain.c b/gfx/drivers_renderchain/null_renderchain.c deleted file mode 100644 index 6da4458bc2..0000000000 --- a/gfx/drivers_renderchain/null_renderchain.c +++ /dev/null @@ -1,135 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2017 - Daniel De Matteis - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include -#include -#include - -#include "../video_driver.h" - -typedef struct null_renderchain -{ - void *empty; -} null_renderchain_t; - -static void null_renderchain_free(void *data) -{ -} - -static void *null_renderchain_new(void) -{ - null_renderchain_t *renderchain = (null_renderchain_t*)calloc(1, sizeof(*renderchain)); - if (!renderchain) - return NULL; - - return renderchain; -} - -static bool null_renderchain_init(void *data, - const void *info, - void *dev_data, - const void *final_viewport_data, - const void *info_data, - bool rgb32 - ) -{ - (void)data; - (void)info; - (void)dev_data; - (void)final_viewport_data; - (void)info_data; - (void)rgb32; - - return true; -} - -static void null_renderchain_set_final_viewport(void *data, - void *renderchain_data, const void *viewport_data) -{ - (void)data; - (void)renderchain_data; - (void)viewport_data; -} - -static bool null_renderchain_render(void *data, const void *frame, - unsigned width, unsigned height, - unsigned pitch, unsigned rotation) -{ - (void)data; - (void)frame; - (void)width; - (void)height; - (void)pitch; - (void)rotation; - - return true; -} - -static bool null_renderchain_add_lut(void *data, - const char *id, const char *path, bool smooth) -{ - (void)data; - (void)id; - (void)path; - (void)smooth; - - return true; -} - -static bool null_renderchain_add_pass(void *data, const void *info_data) -{ - (void)data; - (void)info_data; - - return true; -} - -static void null_renderchain_add_state_tracker(void *data, void *tracker_data) -{ - (void)data; - (void)tracker_data; -} - -static void null_renderchain_convert_geometry( - void *data, const void *info_data, - unsigned *out_width, unsigned *out_height, - unsigned width, unsigned height, - void *final_viewport_data) -{ - (void)data; - (void)info_data; - (void)out_width; - (void)out_height; - (void)width; - (void)height; - (void)final_viewport_data; -} - -d3d_renderchain_driver_t null_d3d_renderchain = { - null_renderchain_free, - null_renderchain_new, - null_renderchain_init, - null_renderchain_set_final_viewport, - null_renderchain_add_pass, - null_renderchain_add_lut, - null_renderchain_add_state_tracker, - null_renderchain_render, - null_renderchain_convert_geometry, - NULL, - NULL, - NULL, - "null", -}; diff --git a/griffin/griffin.c b/griffin/griffin.c index 6bab9b6631..8f73ea6e0c 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -394,7 +394,6 @@ VIDEO DRIVER #include "../gfx/drivers/drm_gfx.c" #endif -#include "../gfx/drivers_renderchain/null_renderchain.c" #include "../gfx/display_servers/dispserv_null.c" #ifdef HAVE_OPENGL From 32c92a9a22b584fa5123b2febb38cfe55de416ae Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 15:04:19 +0200 Subject: [PATCH 419/517] Rename d3dxbuffer_release --- gfx/common/d3d9_common.c | 2 +- gfx/common/d3d9_common.h | 2 +- gfx/drivers_font/xdk360_fonts.cpp | 6 +++--- gfx/drivers_shader/shader_hlsl.c | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index cc0ae1739a..61876b4c5a 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -502,7 +502,7 @@ bool d3d9x_create_font_indirect(void *_dev, return false; } -void d3dxbuffer_release(void *data) +void d3d9x_buffer_release(void *data) { #ifdef HAVE_D3DX LPD3DXBUFFER p = (LPD3DXBUFFER)data; diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index 6709636932..1873a49161 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -598,7 +598,7 @@ void d3d9x_font_draw_text(void *data, void *sprite_data, void *string_data, void d3d9x_font_get_text_metrics(void *data, void *metrics); -void d3dxbuffer_release(void *data); +void d3d9x_buffer_release(void *data); void d3d9x_font_release(void *data); diff --git a/gfx/drivers_font/xdk360_fonts.cpp b/gfx/drivers_font/xdk360_fonts.cpp index 71d0b82fbd..71da31402f 100644 --- a/gfx/drivers_font/xdk360_fonts.cpp +++ b/gfx/drivers_font/xdk360_fonts.cpp @@ -451,7 +451,7 @@ static bool xdk360_video_font_create_shaders(xdk360_video_font_t * font, void *d (void**)&font->s_FontLocals.m_pFontVertexShader )) goto error; - d3dxbuffer_release(pShaderCode); + d3d9x_buffer_release(pShaderCode); if (!d3dx_compile_shader(font_hlsl_d3d9_program, sizeof(font_hlsl_d3d9_program)-1 , NULL, NULL, "main_fragment", "ps.2.0", 0,&pShaderCode, NULL, NULL )) @@ -461,13 +461,13 @@ static bool xdk360_video_font_create_shaders(xdk360_video_font_t * font, void *d (void**)&font->s_FontLocals.m_pFontPixelShader)) goto error; - d3dxbuffer_release(pShaderCode); + d3d9x_buffer_release(pShaderCode); return true; error: if (pShaderCode) - d3dxbuffer_release(pShaderCode); + d3d9x_buffer_release(pShaderCode); d3d9_free_pixel_shader(font->d3d->dev, font->s_FontLocals.m_pFontPixelShader); d3d9_free_vertex_shader(font->d3d->dev, font->s_FontLocals.m_pFontVertexShader); d3d9_vertex_declaration_free(font->s_FontLocals.m_pFontVertexDecl); diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 2b73a92f89..77fe7ff883 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -273,8 +273,8 @@ static bool hlsl_compile_program( d3d_create_pixel_shader(d3dr, (const DWORD*)d3dx_get_buffer_ptr(code_f), (void**)&program->fprg); d3d_create_vertex_shader(d3dr, (const DWORD*)d3dx_get_buffer_ptr(code_v), (void**)&program->vprg); - d3dxbuffer_release((void*)code_f); - d3dxbuffer_release((void*)code_v); + d3d9x_buffer_release((void*)code_f); + d3d9x_buffer_release((void*)code_v); return true; @@ -284,8 +284,8 @@ error: RARCH_ERR("Fragment:\n%s\n", (char*)d3dx_get_buffer_ptr(listing_f)); if (listing_v) RARCH_ERR("Vertex:\n%s\n", (char*)d3dx_get_buffer_ptr(listing_v)); - d3dxbuffer_release((void*)listing_f); - d3dxbuffer_release((void*)listing_v); + d3d9x_buffer_release((void*)listing_f); + d3d9x_buffer_release((void*)listing_v); return false; } From af734939ee913800820bcc34815fdab36793ff69 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 15:26:35 +0200 Subject: [PATCH 420/517] More HLSL work --- gfx/common/d3d9_common.c | 14 +++++ gfx/common/d3d9_common.h | 3 ++ gfx/drivers_shader/shader_hlsl.c | 91 +++++++++++++++----------------- 3 files changed, 61 insertions(+), 47 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index 61876b4c5a..2bdb8984f2 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -611,6 +611,20 @@ const void *d3d9x_get_buffer_ptr(void *data) return NULL; } +void *d3d9x_constant_table_get_constant_by_name(void *_tbl, + void *_handle, void *_name) +{ +#if defined(HAVE_D3DX) + D3DXHANDLE handle = (D3DXHANDLE)_handle; + LPD3DXCONSTANTTABLE consttbl = (LPD3DXCONSTANTTABLE)_tbl; + LPCSTR name = (LPCSTR)_name; + if (consttbl && handle && name) + return consttbl->lpVtbl->GetConstantByName(consttbl, + handle, name); +#endif + return NULL; +} + const bool d3d9x_constant_table_set_float(void *p, void *a, const void *b, float val) diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index 1873a49161..4db01c7f8a 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -630,6 +630,9 @@ const void *d3d9x_get_buffer_ptr(void *data); const bool d3d9x_constant_table_set_float(void *p, void *a, const void *b, float val); +void *d3d9x_constant_table_get_constant_by_name(void *_tbl, + void *_handle, void *_name); + static INLINE INT32 d3d9_get_rgb565_format(void) { #ifdef _XBOX diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 77fe7ff883..05b6c2384f 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -25,6 +25,7 @@ #include "../../defines/d3d_defines.h" #include "../common/d3d_common.h" +#include "../common/d3d9_common.h" #ifdef HAVE_CONFIG_H #include "../../config.h" @@ -48,10 +49,6 @@ #define ID3DXConstantTable_SetFloatArray(p,a,b,c,d) (p)->SetFloatArray(a,b,c,d) #endif -#ifndef ID3DXConstantTable_GetConstantByName -#define ID3DXConstantTable_GetConstantByName(p,a,b) ((p)->GetConstantByName(a, b)) -#endif - #ifndef ID3DXConstantTable_SetMatrix #define ID3DXConstantTable_SetMatrix(p,a,b,c) ((p)->SetMatrix(a,b,c)) #endif @@ -66,10 +63,6 @@ #define ID3DXConstantTable_SetFloatArray(p,a,b,c,d) (p)->lpVtbl->SetFloatArray(p,a,b,c,d) #endif -#ifndef ID3DXConstantTable_GetConstantByName -#define ID3DXConstantTable_GetConstantByName(p,a,b) ((p)->lpVtbl->GetConstantByName(p, a, b)) -#endif - #ifndef ID3DXConstantTable_SetMatrix #define ID3DXConstantTable_SetMatrix(p,a,b,c) ((p)->lpVtbl->SetMatrix(p,a,b,c)) #endif @@ -77,8 +70,6 @@ #endif #define set_param_2f(param, xy, constanttable) if (param) { ID3DXConstantTable_SetFloatArray(constanttable, d3dr, param, xy, 2); } -#define get_constant_by_name(a, b, constanttable) ID3DXConstantTable_GetConstantByName(constanttable, a, b) - struct shader_program_hlsl_data { @@ -208,11 +199,11 @@ static void hlsl_set_params(void *dat, void *shader_data) set_param_2f(hlsl->prg[hlsl->active_idx].out_size_f, out_size, hlsl->prg[hlsl->active_idx].f_ctable); if (hlsl->prg[hlsl->active_idx].frame_cnt_f) - d3dx_constant_table_set_float(hlsl->prg[hlsl->active_idx].f_ctable, + d3d9x_constant_table_set_float(hlsl->prg[hlsl->active_idx].f_ctable, d3dr,hlsl->prg[hlsl->active_idx].frame_cnt_f, frame_cnt); if (hlsl->prg[hlsl->active_idx].frame_dir_f) - d3dx_constant_table_set_float(hlsl->prg[hlsl->active_idx].f_ctable, + d3d9x_constant_table_set_float(hlsl->prg[hlsl->active_idx].f_ctable, d3dr, hlsl->prg[hlsl->active_idx].frame_dir_f, state_manager_frame_is_reversed() ? -1.0 : 1.0); set_param_2f(hlsl->prg[hlsl->active_idx].vid_size_v, ori_size, hlsl->prg[hlsl->active_idx].v_ctable); @@ -220,11 +211,11 @@ static void hlsl_set_params(void *dat, void *shader_data) set_param_2f(hlsl->prg[hlsl->active_idx].out_size_v, out_size, hlsl->prg[hlsl->active_idx].v_ctable); if (hlsl->prg[hlsl->active_idx].frame_cnt_v) - d3dx_constant_table_set_float(hlsl->prg[hlsl->active_idx].v_ctable, + d3d9x_constant_table_set_float(hlsl->prg[hlsl->active_idx].v_ctable, d3dr, hlsl->prg[hlsl->active_idx].frame_cnt_v, frame_cnt); if (hlsl->prg[hlsl->active_idx].frame_dir_v) - d3dx_constant_table_set_float(hlsl->prg[hlsl->active_idx].v_ctable, + d3d9x_constant_table_set_float(hlsl->prg[hlsl->active_idx].v_ctable, d3dr, hlsl->prg[hlsl->active_idx].frame_dir_v, state_manager_frame_is_reversed() ? -1.0 : 1.0); /* TODO - set lookup textures/FBO textures/state parameters/etc */ @@ -249,30 +240,30 @@ static bool hlsl_compile_program( if (program_info->is_file) { - if (!d3dx_compile_shader_from_file(program_info->combined, NULL, NULL, + if (!d3d9x_compile_shader_from_file(program_info->combined, NULL, NULL, "main_fragment", "ps_3_0", 0, &code_f, &listing_f, &program->f_ctable)) goto error; - if (!d3dx_compile_shader_from_file(program_info->combined, NULL, NULL, + if (!d3d9x_compile_shader_from_file(program_info->combined, NULL, NULL, "main_vertex", "vs_3_0", 0, &code_v, &listing_v, &program->v_ctable)) goto error; } else { /* TODO - crashes currently - to do with 'end of line' of stock shader */ - if (!d3dx_compile_shader(program_info->combined, + if (!d3d9x_compile_shader(program_info->combined, strlen(program_info->combined), NULL, NULL, "main_fragment", "ps_3_0", 0, &code_f, &listing_f, &program->f_ctable )) goto error; - if (!d3dx_compile_shader(program_info->combined, + if (!d3d9x_compile_shader(program_info->combined, strlen(program_info->combined), NULL, NULL, "main_vertex", "vs_3_0", 0, &code_v, &listing_v, &program->v_ctable )) goto error; } - d3d_create_pixel_shader(d3dr, (const DWORD*)d3dx_get_buffer_ptr(code_f), (void**)&program->fprg); - d3d_create_vertex_shader(d3dr, (const DWORD*)d3dx_get_buffer_ptr(code_v), (void**)&program->vprg); + d3d9_create_pixel_shader(d3dr, (const DWORD*)d3d9x_get_buffer_ptr(code_f), (void**)&program->fprg); + d3d9_create_vertex_shader(d3dr, (const DWORD*)d3d9x_get_buffer_ptr(code_v), (void**)&program->vprg); d3d9x_buffer_release((void*)code_f); d3d9x_buffer_release((void*)code_v); @@ -281,9 +272,9 @@ static bool hlsl_compile_program( error: RARCH_ERR("Cg/HLSL error:\n"); if (listing_f) - RARCH_ERR("Fragment:\n%s\n", (char*)d3dx_get_buffer_ptr(listing_f)); + RARCH_ERR("Fragment:\n%s\n", (char*)d3d9x_get_buffer_ptr(listing_f)); if (listing_v) - RARCH_ERR("Vertex:\n%s\n", (char*)d3dx_get_buffer_ptr(listing_v)); + RARCH_ERR("Vertex:\n%s\n", (char*)d3d9x_get_buffer_ptr(listing_v)); d3d9x_buffer_release((void*)listing_f); d3d9x_buffer_release((void*)listing_v); @@ -311,17 +302,17 @@ static void hlsl_set_program_attributes(hlsl_shader_data_t *hlsl, unsigned i) if (!hlsl) return; - hlsl->prg[i].vid_size_f = get_constant_by_name(NULL, "$IN.video_size", hlsl->prg[i].f_ctable); - hlsl->prg[i].tex_size_f = get_constant_by_name(NULL, "$IN.texture_size", hlsl->prg[i].f_ctable); - hlsl->prg[i].out_size_f = get_constant_by_name(NULL, "$IN.output_size", hlsl->prg[i].f_ctable); - hlsl->prg[i].frame_cnt_f = get_constant_by_name(NULL, "$IN.frame_count", hlsl->prg[i].f_ctable); - hlsl->prg[i].frame_dir_f = get_constant_by_name(NULL, "$IN.frame_direction", hlsl->prg[i].f_ctable); - hlsl->prg[i].vid_size_v = get_constant_by_name(NULL, "$IN.video_size", hlsl->prg[i].v_ctable); - hlsl->prg[i].tex_size_v = get_constant_by_name(NULL, "$IN.texture_size", hlsl->prg[i].v_ctable); - hlsl->prg[i].out_size_v = get_constant_by_name(NULL, "$IN.output_size", hlsl->prg[i].v_ctable); - hlsl->prg[i].frame_cnt_v = get_constant_by_name(NULL, "$IN.frame_count", hlsl->prg[i].v_ctable); - hlsl->prg[i].frame_dir_v = get_constant_by_name(NULL, "$IN.frame_direction", hlsl->prg[i].v_ctable); - hlsl->prg[i].mvp = get_constant_by_name(NULL, "$modelViewProj", hlsl->prg[i].v_ctable); + hlsl->prg[i].vid_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.video_size"); + hlsl->prg[i].tex_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.texture_size"); + hlsl->prg[i].out_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.output_size"); + hlsl->prg[i].frame_cnt_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.frame_count"); + hlsl->prg[i].frame_dir_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.frame_direction"); + hlsl->prg[i].vid_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.video_size"); + hlsl->prg[i].tex_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.texture_size"); + hlsl->prg[i].out_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.output_size"); + hlsl->prg[i].frame_cnt_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.frame_count"); + hlsl->prg[i].frame_dir_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.frame_direction"); + hlsl->prg[i].mvp = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$modelViewProj"); d3d_matrix_identity(&hlsl->prg[i].mvp_val); } @@ -352,7 +343,8 @@ static bool hlsl_load_plain(hlsl_shader_data_t *hlsl, const char *path) if (!hlsl_load_stock(hlsl)) return false; - hlsl->cg_shader = (struct video_shader*)calloc(1, sizeof(*hlsl->cg_shader)); + hlsl->cg_shader = (struct video_shader*) + calloc(1, sizeof(*hlsl->cg_shader)); if (!hlsl->cg_shader) return false; @@ -389,18 +381,18 @@ static void hlsl_deinit_progs(hlsl_shader_data_t *hlsl) for (i = 1; i < RARCH_HLSL_MAX_SHADERS; i++) { if (hlsl->prg[i].fprg && hlsl->prg[i].fprg != hlsl->prg[0].fprg) - d3d_free_pixel_shader(hlsl->dev, hlsl->prg[i].fprg); + d3d9_free_pixel_shader(hlsl->dev, hlsl->prg[i].fprg); if (hlsl->prg[i].vprg && hlsl->prg[i].vprg != hlsl->prg[0].vprg) - d3d_free_vertex_shader(hlsl->dev, hlsl->prg[i].vprg); + d3d9_free_vertex_shader(hlsl->dev, hlsl->prg[i].vprg); hlsl->prg[i].fprg = NULL; hlsl->prg[i].vprg = NULL; } if (hlsl->prg[0].fprg) - d3d_free_pixel_shader(hlsl->dev, hlsl->prg[0].fprg); + d3d9_free_pixel_shader(hlsl->dev, hlsl->prg[0].fprg); if (hlsl->prg[0].vprg) - d3d_free_vertex_shader(hlsl->dev, hlsl->prg[0].vprg); + d3d9_free_vertex_shader(hlsl->dev, hlsl->prg[0].vprg); hlsl->prg[0].fprg = NULL; hlsl->prg[0].vprg = NULL; @@ -421,7 +413,8 @@ static bool hlsl_load_preset(hlsl_shader_data_t *hlsl, const char *path) goto error; if (!hlsl->cg_shader) - hlsl->cg_shader = (struct video_shader*)calloc(1, sizeof(*hlsl->cg_shader)); + hlsl->cg_shader = (struct video_shader*)calloc + (1, sizeof(*hlsl->cg_shader)); if (!hlsl->cg_shader) goto error; @@ -435,7 +428,8 @@ static bool hlsl_load_preset(hlsl_shader_data_t *hlsl, const char *path) if (hlsl->cg_shader->passes > RARCH_HLSL_MAX_SHADERS - 3) { - RARCH_WARN("Too many shaders ... Capping shader amount to %d.\n", RARCH_HLSL_MAX_SHADERS - 3); + RARCH_WARN("Too many shaders ... " + "Capping shader amount to %d.\n", RARCH_HLSL_MAX_SHADERS - 3); hlsl->cg_shader->passes = RARCH_HLSL_MAX_SHADERS - 3; } @@ -486,8 +480,8 @@ static void *hlsl_init(void *data, const char *path) for(i = 1; i <= hlsl->cg_shader->passes; i++) hlsl_set_program_attributes(hlsl, i); - d3d_set_vertex_shader(hlsl->dev, 1, hlsl->prg[1].vprg); - d3d_set_pixel_shader(hlsl->dev, hlsl->prg[1].fprg); + d3d9_set_vertex_shader(hlsl->dev, 1, hlsl->prg[1].vprg); + d3d9_set_pixel_shader(hlsl->dev, hlsl->prg[1].fprg); return hlsl; @@ -515,7 +509,8 @@ static void hlsl_deinit(void *data) free(hlsl); } -static void hlsl_use(void *data, void *shader_data, unsigned idx, bool set_active) +static void hlsl_use(void *data, void *shader_data, + unsigned idx, bool set_active) { hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)shader_data; LPDIRECT3DDEVICE9 d3dr = hlsl ? (LPDIRECT3DDEVICE9)hlsl->dev : NULL; @@ -526,8 +521,8 @@ static void hlsl_use(void *data, void *shader_data, unsigned idx, bool set_activ if (set_active) hlsl->active_idx = idx; - d3d_set_vertex_shader(d3dr, idx, hlsl->prg[idx].vprg); - d3d_set_pixel_shader(d3dr, hlsl->prg[idx].fprg); + d3d9_set_vertex_shader(d3dr, idx, hlsl->prg[idx].vprg); + d3d9_set_pixel_shader(d3dr, hlsl->prg[idx].fprg); } static unsigned hlsl_num(void *data) @@ -551,7 +546,8 @@ static bool hlsl_filter_type(void *data, unsigned idx, bool *smooth) return false; } -static void hlsl_shader_scale(void *data, unsigned idx, struct gfx_fbo_scale *scale) +static void hlsl_shader_scale(void *data, unsigned idx, + struct gfx_fbo_scale *scale) { hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)data; if (hlsl && idx) @@ -563,7 +559,8 @@ static void hlsl_shader_scale(void *data, unsigned idx, struct gfx_fbo_scale *sc static bool hlsl_set_mvp(void *data, void *shader_data, const void *mat_data) { hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)shader_data; - LPDIRECT3DDEVICE9 d3dr = hlsl ? (LPDIRECT3DDEVICE9)hlsl->dev : NULL; + LPDIRECT3DDEVICE9 d3dr = hlsl ? + (LPDIRECT3DDEVICE9)hlsl->dev : NULL; const math_matrix_4x4 *mat = (const math_matrix_4x4*)mat_data; if (!hlsl || !hlsl->prg[hlsl->active_idx].mvp) From 0c4a3a83ce40e0339195d296a3753198fea8d33d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 15:32:47 +0200 Subject: [PATCH 421/517] (HLSL) Fix implicit declaration --- gfx/drivers_renderchain/d3d9_hlsl_renderchain.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c index ab3eaacc3e..21bfcf7539 100644 --- a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c +++ b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c @@ -30,6 +30,7 @@ #include "../video_driver.h" #include "../../configuration.h" +#include "../../retroarch.h" #include "../../verbosity.h" typedef struct hlsl_d3d9_renderchain From 282b4ba21b188e33f7943cae9cf34ce902f33ec9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 15:40:43 +0200 Subject: [PATCH 422/517] (d3d9_common.c) Cleanups --- gfx/common/d3d9_common.c | 71 ++++++---------------------------------- gfx/common/d3d9_common.h | 49 ++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 70 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index 2bdb8984f2..ece658804b 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -249,15 +249,12 @@ static void *d3d9_texture_new_from_file( PALETTEENTRY *palette) { void *buf = NULL; - HRESULT hr = D3D9CreateTextureFromFile((LPDIRECT3DDEVICE9)dev, - path, width, height, miplevels, usage, format, - (D3DPOOL)pool, filter, mipfilter, color_key, - (D3DXIMAGE_INFO*)src_info_data, - palette, (struct IDirect3DTexture9**)&buf); - - if (FAILED(hr)) + if (FAILED(D3D9CreateTextureFromFile((LPDIRECT3DDEVICE9)dev, + path, width, height, miplevels, usage, format, + (D3DPOOL)pool, filter, mipfilter, color_key, + (D3DXIMAGE_INFO*)src_info_data, + palette, (struct IDirect3DTexture9**)&buf))) return NULL; - return buf; } #endif @@ -289,13 +286,13 @@ void *d3d9_texture_new(void *_dev, if (want_mipmap) usage |= D3DUSAGE_AUTOGENMIPMAP; #endif + if (FAILED(IDirect3DDevice9_CreateTexture(dev, width, height, miplevels, usage, (D3DFORMAT)format, (D3DPOOL)pool, (struct IDirect3DTexture9**)&buf, NULL))) return NULL; - return buf; } @@ -312,9 +309,10 @@ void *d3d9_vertex_buffer_new(void *_dev, usage = D3DUSAGE_SOFTWAREPROCESSING; #endif - if (FAILED(IDirect3DDevice9_CreateVertexBuffer(dev, length, usage, fvf, - (D3DPOOL)pool, - (LPDIRECT3DVERTEXBUFFER9*)&buf, NULL))) + if (FAILED(IDirect3DDevice9_CreateVertexBuffer( + dev, length, usage, fvf, + (D3DPOOL)pool, + (LPDIRECT3DVERTEXBUFFER9*)&buf, NULL))) return NULL; return buf; @@ -339,45 +337,6 @@ void d3d9_vertex_buffer_free(void *vertex_data, void *vertex_declaration) } } - -bool d3d9_device_create_offscreen_plain_surface( - void *_dev, - unsigned width, - unsigned height, - unsigned format, - unsigned pool, - void **surf_data, - void *data) -{ -#ifndef _XBOX - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (SUCCEEDED(IDirect3DDevice9_CreateOffscreenPlainSurface(dev, - width, height, - (D3DFORMAT)format, (D3DPOOL)pool, - (LPDIRECT3DSURFACE9*)surf_data, - (HANDLE*)data))) - return true; -#endif - - return false; -} - -bool d3d9_device_get_render_target_data(void *_dev, - void *_src, void *_dst) -{ -#ifndef _XBOX - LPDIRECT3DSURFACE9 src = (LPDIRECT3DSURFACE9)_src; - LPDIRECT3DSURFACE9 dst = (LPDIRECT3DSURFACE9)_dst; - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (dev && - SUCCEEDED(IDirect3DDevice9_GetRenderTargetData( - dev, src, dst))) - return true; -#endif - - return false; -} - static bool d3d9_reset_internal(void *data, D3DPRESENT_PARAMETERS *d3dpp ) @@ -478,16 +437,6 @@ bool d3d9_reset(void *dev, void *d3dpp) return false; } -void d3d9_device_free(void *_dev, void *_pd3d) -{ - LPDIRECT3D9 pd3d = (LPDIRECT3D9)_pd3d; - LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; - if (dev) - IDirect3DDevice9_Release(dev); - if (pd3d) - IDirect3D9_Release(pd3d); -} - bool d3d9x_create_font_indirect(void *_dev, void *desc, void **font_data) { diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index 4db01c7f8a..2b49ab2a67 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -448,8 +448,22 @@ static INLINE void d3d9_surface_free(LPDIRECT3DSURFACE9 surf) IDirect3DSurface9_Release(surf); } -bool d3d9_device_get_render_target_data(void *dev, - void *_src, void *_dst); +static INLINE bool d3d9_device_get_render_target_data( + void *_dev, + void *_src, void *_dst) +{ +#ifndef _XBOX + LPDIRECT3DSURFACE9 src = (LPDIRECT3DSURFACE9)_src; + LPDIRECT3DSURFACE9 dst = (LPDIRECT3DSURFACE9)_dst; + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev && + SUCCEEDED(IDirect3DDevice9_GetRenderTargetData( + dev, src, dst))) + return true; +#endif + + return false; +} static INLINE bool d3d9_device_get_render_target( LPDIRECT3DDEVICE9 dev, @@ -490,17 +504,26 @@ static INLINE bool d3d9_get_render_state( return true; } -void d3d9_device_set_render_target(void *dev, unsigned idx, - void *data); - -bool d3d9_device_create_offscreen_plain_surface( - void *dev, +static INLINE bool d3d9_device_create_offscreen_plain_surface( + void *_dev, unsigned width, unsigned height, unsigned format, unsigned pool, void **surf_data, - void *data); + void *data) +{ +#ifndef _XBOX + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (SUCCEEDED(IDirect3DDevice9_CreateOffscreenPlainSurface(dev, + width, height, + (D3DFORMAT)format, (D3DPOOL)pool, + (LPDIRECT3DSURFACE9*)surf_data, + (HANDLE*)data))) + return true; +#endif + return false; +} static INLINE bool d3d9_surface_lock_rect(LPDIRECT3DSURFACE9 surf, D3DLOCKED_RECT *data2) @@ -564,7 +587,15 @@ static INLINE bool d3d9_device_get_backbuffer( return false; } -void d3d9_device_free(void *dev, void *pd3d); +static INLINE void d3d9_device_free(void *_dev, void *_pd3d) +{ + LPDIRECT3D9 pd3d = (LPDIRECT3D9)_pd3d; + LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)_dev; + if (dev) + IDirect3DDevice9_Release(dev); + if (pd3d) + IDirect3D9_Release(pd3d); +} void *d3d9_create(void); From d03ff06cf3320366b5af7208a9d40a3074afbef2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 15:51:24 +0200 Subject: [PATCH 423/517] (HLSL) Cleanups --- gfx/drivers_shader/shader_hlsl.c | 110 +++++++++++++++++-------------- 1 file changed, 61 insertions(+), 49 deletions(-) diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 05b6c2384f..28d1aa775a 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -69,7 +69,7 @@ #endif -#define set_param_2f(param, xy, constanttable) if (param) { ID3DXConstantTable_SetFloatArray(constanttable, d3dr, param, xy, 2); } +#define set_param_2f(param, xy, constanttable) ID3DXConstantTable_SetFloatArray(constanttable, d3dr, param, xy, 2) struct shader_program_hlsl_data { @@ -158,65 +158,77 @@ static void hlsl_set_uniform_parameter( static void hlsl_set_params(void *dat, void *shader_data) { float ori_size[2], tex_size[2], out_size[2]; - video_shader_ctx_params_t *params = (video_shader_ctx_params_t*)dat; - void *data = params->data; - unsigned width = params->width; - unsigned height = params->height; - unsigned tex_width = params->tex_width; - unsigned tex_height = params->tex_height; - unsigned out_width = params->out_width; - unsigned out_height = params->out_height; - unsigned frame_count = params->frame_counter; - const void *_info = params->info; - const void *_prev_info = params->prev_info; - const void *_feedback_info = params->feedback_info; - const void *_fbo_info = params->fbo_info; - unsigned fbo_info_cnt = params->fbo_info_cnt; - float frame_cnt = frame_count; - const struct video_tex_info *info = (const struct video_tex_info*)_info; - const struct video_tex_info *prev_info = (const struct video_tex_info*)_prev_info; - const struct video_tex_info *fbo_info = (const struct video_tex_info*)_fbo_info; - hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)shader_data; - LPDIRECT3DDEVICE9 d3dr = (LPDIRECT3DDEVICE9)hlsl->dev; + video_shader_ctx_params_t *params = (video_shader_ctx_params_t*)dat; + void *data = params->data; + unsigned width = params->width; + unsigned height = params->height; + unsigned tex_width = params->tex_width; + unsigned tex_height = params->tex_height; + unsigned out_width = params->out_width; + unsigned out_height = params->out_height; + unsigned frame_count = params->frame_counter; + const void *_info = params->info; + const void *_prev_info = params->prev_info; + const void *_feedback_info = params->feedback_info; + const void *_fbo_info = params->fbo_info; + unsigned fbo_info_cnt = params->fbo_info_cnt; + float frame_cnt = frame_count; + const struct video_tex_info *info = (const struct video_tex_info*)_info; + const struct video_tex_info *prev_info = (const struct video_tex_info*)_prev_info; + const struct video_tex_info *fbo_info = (const struct video_tex_info*)_fbo_info; + hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)shader_data; + LPDIRECT3DDEVICE9 d3dr = (LPDIRECT3DDEVICE9)hlsl->dev; + struct shader_program_hlsl_data *program = NULL; if (!hlsl || !d3dr) return; - ori_size[0] = (float)width; - ori_size[1] = (float)height; - tex_size[0] = (float)tex_width; - tex_size[1] = (float)tex_height; - out_size[0] = (float)out_width; - out_size[1] = (float)out_height; + program = &hlsl->prg[hlsl->active_idx]; - ID3DXConstantTable_SetDefaults( - hlsl->prg[hlsl->active_idx].f_ctable, d3dr); - ID3DXConstantTable_SetDefaults( - hlsl->prg[hlsl->active_idx].v_ctable, d3dr); + if (!program) + return; - set_param_2f(hlsl->prg[hlsl->active_idx].vid_size_f, ori_size, hlsl->prg[hlsl->active_idx].f_ctable); - set_param_2f(hlsl->prg[hlsl->active_idx].tex_size_f, tex_size, hlsl->prg[hlsl->active_idx].f_ctable); - set_param_2f(hlsl->prg[hlsl->active_idx].out_size_f, out_size, hlsl->prg[hlsl->active_idx].f_ctable); + ori_size[0] = (float)width; + ori_size[1] = (float)height; + tex_size[0] = (float)tex_width; + tex_size[1] = (float)tex_height; + out_size[0] = (float)out_width; + out_size[1] = (float)out_height; - if (hlsl->prg[hlsl->active_idx].frame_cnt_f) - d3d9x_constant_table_set_float(hlsl->prg[hlsl->active_idx].f_ctable, - d3dr,hlsl->prg[hlsl->active_idx].frame_cnt_f, frame_cnt); + ID3DXConstantTable_SetDefaults(program->f_ctable, d3dr); + ID3DXConstantTable_SetDefaults(program->v_ctable, d3dr); - if (hlsl->prg[hlsl->active_idx].frame_dir_f) - d3d9x_constant_table_set_float(hlsl->prg[hlsl->active_idx].f_ctable, - d3dr, hlsl->prg[hlsl->active_idx].frame_dir_f, state_manager_frame_is_reversed() ? -1.0 : 1.0); + if (program->vid_size_f) + set_param_2f(program->vid_size_f, ori_size, program->f_ctable); + if (program->tex_size_f) + set_param_2f(program->tex_size_f, tex_size, program->f_ctable); + if (program->out_size_f) + set_param_2f(program->out_size_f, out_size, program->f_ctable); - set_param_2f(hlsl->prg[hlsl->active_idx].vid_size_v, ori_size, hlsl->prg[hlsl->active_idx].v_ctable); - set_param_2f(hlsl->prg[hlsl->active_idx].tex_size_v, tex_size, hlsl->prg[hlsl->active_idx].v_ctable); - set_param_2f(hlsl->prg[hlsl->active_idx].out_size_v, out_size, hlsl->prg[hlsl->active_idx].v_ctable); + if (program->frame_cnt_f) + d3d9x_constant_table_set_float(program->f_ctable, + d3dr, program->frame_cnt_f, frame_cnt); - if (hlsl->prg[hlsl->active_idx].frame_cnt_v) - d3d9x_constant_table_set_float(hlsl->prg[hlsl->active_idx].v_ctable, - d3dr, hlsl->prg[hlsl->active_idx].frame_cnt_v, frame_cnt); + if (program->frame_dir_f) + d3d9x_constant_table_set_float(program->f_ctable, + d3dr, program->frame_dir_f, + state_manager_frame_is_reversed() ? -1.0 : 1.0); - if (hlsl->prg[hlsl->active_idx].frame_dir_v) - d3d9x_constant_table_set_float(hlsl->prg[hlsl->active_idx].v_ctable, - d3dr, hlsl->prg[hlsl->active_idx].frame_dir_v, state_manager_frame_is_reversed() ? -1.0 : 1.0); + if (program->vid_size_v) + set_param_2f(program->vid_size_v, ori_size, program->v_ctable); + if (program->tex_size_v) + set_param_2f(program->tex_size_v, tex_size, program->v_ctable); + if (program->out_size_v) + set_param_2f(program->out_size_v, out_size, program->v_ctable); + + if (program->frame_cnt_v) + d3d9x_constant_table_set_float(program->v_ctable, + d3dr, program->frame_cnt_v, frame_cnt); + + if (program->frame_dir_v) + d3d9x_constant_table_set_float(program->v_ctable, + d3dr, program->frame_dir_v, + state_manager_frame_is_reversed() ? -1.0 : 1.0); /* TODO - set lookup textures/FBO textures/state parameters/etc */ } From d7fa70927c63dc9d08c88e16ffe4586895e05c77 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 15:55:26 +0200 Subject: [PATCH 424/517] (HLSL) Cleanups --- gfx/drivers_shader/shader_hlsl.c | 41 ++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 28d1aa775a..552ea9d2bc 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -524,17 +524,23 @@ static void hlsl_deinit(void *data) static void hlsl_use(void *data, void *shader_data, unsigned idx, bool set_active) { - hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)shader_data; - LPDIRECT3DDEVICE9 d3dr = hlsl ? (LPDIRECT3DDEVICE9)hlsl->dev : NULL; + hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)shader_data; + LPDIRECT3DDEVICE9 d3dr = hlsl ? (LPDIRECT3DDEVICE9)hlsl->dev : NULL; + struct shader_program_hlsl_data *program = NULL; - if (!hlsl || !hlsl->prg[idx].vprg || !hlsl->prg[idx].fprg) + if (!hlsl) + return; + + program = &hlsl->prg[idx]; + + if (!program || !program->vprg || !program->fprg) return; if (set_active) - hlsl->active_idx = idx; + hlsl->active_idx = idx; - d3d9_set_vertex_shader(d3dr, idx, hlsl->prg[idx].vprg); - d3d9_set_pixel_shader(d3dr, hlsl->prg[idx].fprg); + d3d9_set_vertex_shader(d3dr, idx, program->vprg); + d3d9_set_pixel_shader(d3dr, program->fprg); } static unsigned hlsl_num(void *data) @@ -563,24 +569,29 @@ static void hlsl_shader_scale(void *data, unsigned idx, { hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)data; if (hlsl && idx) - *scale = hlsl->cg_shader->pass[idx - 1].fbo; + *scale = hlsl->cg_shader->pass[idx - 1].fbo; else - scale->valid = false; + scale->valid = false; } static bool hlsl_set_mvp(void *data, void *shader_data, const void *mat_data) { - hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)shader_data; - LPDIRECT3DDEVICE9 d3dr = hlsl ? + hlsl_shader_data_t *hlsl = (hlsl_shader_data_t*)shader_data; + LPDIRECT3DDEVICE9 d3dr = hlsl ? (LPDIRECT3DDEVICE9)hlsl->dev : NULL; - const math_matrix_4x4 *mat = (const math_matrix_4x4*)mat_data; + const math_matrix_4x4 *mat = (const math_matrix_4x4*)mat_data; + struct shader_program_hlsl_data *program = NULL; - if (!hlsl || !hlsl->prg[hlsl->active_idx].mvp) + if (!hlsl) return false; - ID3DXConstantTable_SetMatrix(hlsl->prg[hlsl->active_idx].v_ctable, d3dr, - hlsl->prg[hlsl->active_idx].mvp, - &hlsl->prg[hlsl->active_idx].mvp_val); + program = &hlsl->prg[hlsl->active_idx]; + + if (!program || !program->mvp) + return false; + + ID3DXConstantTable_SetMatrix(program->v_ctable, d3dr, + program->mvp, &program->mvp_val); return true; } From 8d02343b53d67e058e7ef0791e15290126663402 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 15:58:00 +0200 Subject: [PATCH 425/517] (HLSL) Cleanups --- gfx/drivers_shader/shader_hlsl.c | 38 ++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 552ea9d2bc..696983ef3b 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -311,21 +311,35 @@ static bool hlsl_load_stock(hlsl_shader_data_t *hlsl) static void hlsl_set_program_attributes(hlsl_shader_data_t *hlsl, unsigned i) { + struct shader_program_hlsl_data *program = NULL; if (!hlsl) return; - hlsl->prg[i].vid_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.video_size"); - hlsl->prg[i].tex_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.texture_size"); - hlsl->prg[i].out_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.output_size"); - hlsl->prg[i].frame_cnt_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.frame_count"); - hlsl->prg[i].frame_dir_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].f_ctable, NULL, "$IN.frame_direction"); - hlsl->prg[i].vid_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.video_size"); - hlsl->prg[i].tex_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.texture_size"); - hlsl->prg[i].out_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.output_size"); - hlsl->prg[i].frame_cnt_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.frame_count"); - hlsl->prg[i].frame_dir_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$IN.frame_direction"); - hlsl->prg[i].mvp = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(hlsl->prg[i].v_ctable, NULL, "$modelViewProj"); - d3d_matrix_identity(&hlsl->prg[i].mvp_val); + program = &hlsl->prg[i]; + + if (!program) + return; + + if (program->f_ctable) + { + hlsl->prg[i].vid_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.video_size"); + hlsl->prg[i].tex_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.texture_size"); + hlsl->prg[i].out_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.output_size"); + hlsl->prg[i].frame_cnt_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.frame_count"); + hlsl->prg[i].frame_dir_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.frame_direction"); + } + + if (program->v_ctable) + { + hlsl->prg[i].vid_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.video_size"); + hlsl->prg[i].tex_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.texture_size"); + hlsl->prg[i].out_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.output_size"); + hlsl->prg[i].frame_cnt_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.frame_count"); + hlsl->prg[i].frame_dir_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.frame_direction"); + hlsl->prg[i].mvp = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$modelViewProj"); + } + + d3d_matrix_identity(&program->mvp_val); } static bool hlsl_load_shader(hlsl_shader_data_t *hlsl, From dbb9a43abed64405b17c79821b8ea1bfde4a43c6 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 15:59:09 +0200 Subject: [PATCH 426/517] (HLSL) Cleanups --- gfx/drivers_shader/shader_hlsl.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 696983ef3b..3516fdbe2b 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -322,21 +322,21 @@ static void hlsl_set_program_attributes(hlsl_shader_data_t *hlsl, unsigned i) if (program->f_ctable) { - hlsl->prg[i].vid_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.video_size"); - hlsl->prg[i].tex_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.texture_size"); - hlsl->prg[i].out_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.output_size"); - hlsl->prg[i].frame_cnt_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.frame_count"); - hlsl->prg[i].frame_dir_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.frame_direction"); + program->vid_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.video_size"); + program->tex_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.texture_size"); + program->out_size_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.output_size"); + program->frame_cnt_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.frame_count"); + program->frame_dir_f = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->f_ctable, NULL, "$IN.frame_direction"); } if (program->v_ctable) { - hlsl->prg[i].vid_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.video_size"); - hlsl->prg[i].tex_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.texture_size"); - hlsl->prg[i].out_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.output_size"); - hlsl->prg[i].frame_cnt_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.frame_count"); - hlsl->prg[i].frame_dir_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.frame_direction"); - hlsl->prg[i].mvp = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$modelViewProj"); + program->vid_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.video_size"); + program->tex_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.texture_size"); + program->out_size_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.output_size"); + program->frame_cnt_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.frame_count"); + program->frame_dir_v = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$IN.frame_direction"); + program->mvp = (D3DXHANDLE)d3d9x_constant_table_get_constant_by_name(program->v_ctable, NULL, "$modelViewProj"); } d3d_matrix_identity(&program->mvp_val); From 0e5ccd7c153fbb2e88815db4d73aac821aab7d1f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 17:04:30 +0200 Subject: [PATCH 427/517] Create d3d9x_constant_table_set_matrix --- gfx/common/d3d9_common.c | 13 +++++++++++++ gfx/common/d3d9_common.h | 3 +++ gfx/drivers_shader/shader_hlsl.c | 8 ++------ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index ece658804b..e259749389 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -574,6 +574,19 @@ void *d3d9x_constant_table_get_constant_by_name(void *_tbl, return NULL; } +void d3d9x_constant_table_set_matrix(LPDIRECT3DDEVICE9 dev, + void *p, + void *data, const void *_matrix) +{ +#if defined(HAVE_D3DX) + LPD3DXCONSTANTTABLE consttbl = (LPD3DXCONSTANTTABLE)p; + D3DXHANDLE handle = (D3DXHANDLE)data; + const D3DXMATRIX *matrix = (const D3DXMATRIX*)matrix; + if (consttbl && dev && handle) + consttbl->lpVtbl->SetMatrix(consttbl, dev, handle, matrix); +#endif +} + const bool d3d9x_constant_table_set_float(void *p, void *a, const void *b, float val) diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index 2b49ab2a67..f10246f3c1 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -656,6 +656,9 @@ bool d3d9x_compile_shader_from_file( void *pperrormsgs, void *ppconstanttable); +void d3d9x_constant_table_set_matrix(LPDIRECT3DDEVICE9 dev, + void *p, void *data, const void *matrix); + const void *d3d9x_get_buffer_ptr(void *data); const bool d3d9x_constant_table_set_float(void *p, diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 3516fdbe2b..3008155931 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -63,10 +63,6 @@ #define ID3DXConstantTable_SetFloatArray(p,a,b,c,d) (p)->lpVtbl->SetFloatArray(p,a,b,c,d) #endif -#ifndef ID3DXConstantTable_SetMatrix -#define ID3DXConstantTable_SetMatrix(p,a,b,c) ((p)->lpVtbl->SetMatrix(p,a,b,c)) -#endif - #endif #define set_param_2f(param, xy, constanttable) ID3DXConstantTable_SetFloatArray(constanttable, d3dr, param, xy, 2) @@ -604,8 +600,8 @@ static bool hlsl_set_mvp(void *data, void *shader_data, const void *mat_data) if (!program || !program->mvp) return false; - ID3DXConstantTable_SetMatrix(program->v_ctable, d3dr, - program->mvp, &program->mvp_val); + d3d9x_constant_table_set_matrix(d3dr, program->v_ctable, + (void*)program->mvp, &program->mvp_val); return true; } From bd3f39b005519427f842e04451f702404b83899d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 17:04:59 +0200 Subject: [PATCH 428/517] Remove SetMatrix macro --- gfx/drivers_shader/shader_hlsl.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 3008155931..74640fe97c 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -49,10 +49,6 @@ #define ID3DXConstantTable_SetFloatArray(p,a,b,c,d) (p)->SetFloatArray(a,b,c,d) #endif -#ifndef ID3DXConstantTable_SetMatrix -#define ID3DXConstantTable_SetMatrix(p,a,b,c) ((p)->SetMatrix(a,b,c)) -#endif - #else #ifndef ID3DXConstantTable_SetDefaults From a6fcd9dae01b8b9882acb153ee1c782418ee9d95 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 17:09:11 +0200 Subject: [PATCH 429/517] Create d3d9x_constant_table_set_defaults --- gfx/common/d3d9_common.c | 10 ++++++++++ gfx/common/d3d9_common.h | 3 +++ gfx/drivers_shader/shader_hlsl.c | 12 ++---------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index e259749389..736cb577f1 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -574,6 +574,16 @@ void *d3d9x_constant_table_get_constant_by_name(void *_tbl, return NULL; } +void d3d9x_constant_table_set_defaults(LPDIRECT3DDEVICE9 dev, + void *p) +{ +#if defined(HAVE_D3DX) + LPD3DXCONSTANTTABLE consttbl = (LPD3DXCONSTANTTABLE)p; + if (consttbl && dev) + consttbl->lpVtbl->SetDefaults(consttbl, dev); +#endif +} + void d3d9x_constant_table_set_matrix(LPDIRECT3DDEVICE9 dev, void *p, void *data, const void *_matrix) diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index f10246f3c1..9d7c3cba1f 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -656,6 +656,9 @@ bool d3d9x_compile_shader_from_file( void *pperrormsgs, void *ppconstanttable); +void d3d9x_constant_table_set_defaults(LPDIRECT3DDEVICE9 dev, + void *p); + void d3d9x_constant_table_set_matrix(LPDIRECT3DDEVICE9 dev, void *p, void *data, const void *matrix); diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 74640fe97c..757c02779e 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -41,20 +41,12 @@ #ifdef __cplusplus -#ifndef ID3DXConstantTable_SetDefaults -#define ID3DXConstantTable_SetDefaults(p,a) (p)->SetDefaults(a); -#endif - #ifndef ID3DXConstantTable_SetFloatArray #define ID3DXConstantTable_SetFloatArray(p,a,b,c,d) (p)->SetFloatArray(a,b,c,d) #endif #else -#ifndef ID3DXConstantTable_SetDefaults -#define ID3DXConstantTable_SetDefaults(p,a) (p)->lpVtbl->SetDefaults(p,a) -#endif - #ifndef ID3DXConstantTable_SetFloatArray #define ID3DXConstantTable_SetFloatArray(p,a,b,c,d) (p)->lpVtbl->SetFloatArray(p,a,b,c,d) #endif @@ -187,8 +179,8 @@ static void hlsl_set_params(void *dat, void *shader_data) out_size[0] = (float)out_width; out_size[1] = (float)out_height; - ID3DXConstantTable_SetDefaults(program->f_ctable, d3dr); - ID3DXConstantTable_SetDefaults(program->v_ctable, d3dr); + d3d9x_constant_table_set_defaults(d3dr, program->f_ctable); + d3d9x_constant_table_set_defaults(d3dr, program->v_ctable); if (program->vid_size_f) set_param_2f(program->vid_size_f, ori_size, program->f_ctable); From 830068e5bda787b4601dbde6f0f12711f04d8340 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 17:20:08 +0200 Subject: [PATCH 430/517] Create d3d9x_constant_table_set_float_array --- gfx/common/d3d9_common.c | 13 +++++++++++++ gfx/common/d3d9_common.h | 3 +++ gfx/drivers_shader/shader_hlsl.c | 28 ++++++---------------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index 736cb577f1..549eb918b2 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -574,6 +574,19 @@ void *d3d9x_constant_table_get_constant_by_name(void *_tbl, return NULL; } +void d3d9x_constant_table_set_float_array(LPDIRECT3DDEVICE9 dev, + void *p, void *_handle, const void *_pf, unsigned count) +{ +#if defined(HAVE_D3DX) + LPD3DXCONSTANTTABLE consttbl = (LPD3DXCONSTANTTABLE)p; + D3DXHANDLE handle = (D3DXHANDLE)_handle; + CONST FLOAT *pf = (CONST FLOAT*)_pf; + if (consttbl && dev) + consttbl->lpVtbl->SetFloatArray(consttbl, dev, handle, pf, + (UINT)count); +#endif +} + void d3d9x_constant_table_set_defaults(LPDIRECT3DDEVICE9 dev, void *p) { diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index 9d7c3cba1f..91331033a6 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -656,6 +656,9 @@ bool d3d9x_compile_shader_from_file( void *pperrormsgs, void *ppconstanttable); +void d3d9x_constant_table_set_float_array(LPDIRECT3DDEVICE9 dev, + void *p, void *_handle, const void *_pf, unsigned count); + void d3d9x_constant_table_set_defaults(LPDIRECT3DDEVICE9 dev, void *p); diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 757c02779e..1500f2941c 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -39,22 +39,6 @@ #include "../drivers/d3d_shaders/opaque.hlsl.d3d9.h" #include "shader_hlsl.h" -#ifdef __cplusplus - -#ifndef ID3DXConstantTable_SetFloatArray -#define ID3DXConstantTable_SetFloatArray(p,a,b,c,d) (p)->SetFloatArray(a,b,c,d) -#endif - -#else - -#ifndef ID3DXConstantTable_SetFloatArray -#define ID3DXConstantTable_SetFloatArray(p,a,b,c,d) (p)->lpVtbl->SetFloatArray(p,a,b,c,d) -#endif - -#endif - -#define set_param_2f(param, xy, constanttable) ID3DXConstantTable_SetFloatArray(constanttable, d3dr, param, xy, 2) - struct shader_program_hlsl_data { LPDIRECT3DVERTEXSHADER9 vprg; @@ -183,11 +167,11 @@ static void hlsl_set_params(void *dat, void *shader_data) d3d9x_constant_table_set_defaults(d3dr, program->v_ctable); if (program->vid_size_f) - set_param_2f(program->vid_size_f, ori_size, program->f_ctable); + d3d9x_constant_table_set_float_array(d3dr, program->f_ctable, (void*)program->vid_size_f, ori_size, 2); if (program->tex_size_f) - set_param_2f(program->tex_size_f, tex_size, program->f_ctable); + d3d9x_constant_table_set_float_array(d3dr, program->f_ctable, (void*)program->tex_size_f, tex_size, 2); if (program->out_size_f) - set_param_2f(program->out_size_f, out_size, program->f_ctable); + d3d9x_constant_table_set_float_array(d3dr, program->f_ctable, (void*)program->out_size_f, out_size, 2); if (program->frame_cnt_f) d3d9x_constant_table_set_float(program->f_ctable, @@ -199,11 +183,11 @@ static void hlsl_set_params(void *dat, void *shader_data) state_manager_frame_is_reversed() ? -1.0 : 1.0); if (program->vid_size_v) - set_param_2f(program->vid_size_v, ori_size, program->v_ctable); + d3d9x_constant_table_set_float_array(d3dr, program->v_ctable, (void*)program->vid_size_v, ori_size, 2); if (program->tex_size_v) - set_param_2f(program->tex_size_v, tex_size, program->v_ctable); + d3d9x_constant_table_set_float_array(d3dr, program->v_ctable, (void*)program->tex_size_v, tex_size, 2); if (program->out_size_v) - set_param_2f(program->out_size_v, out_size, program->v_ctable); + d3d9x_constant_table_set_float_array(d3dr, program->v_ctable, (void*)program->out_size_v, out_size, 2); if (program->frame_cnt_v) d3d9x_constant_table_set_float(program->v_ctable, From 95ecbc085991e86d7b5042a9ac130598c8d892ae Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 17:22:02 +0200 Subject: [PATCH 431/517] Update --- gfx/common/d3d9_common.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index 549eb918b2..dfaff53ede 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -31,13 +31,8 @@ #include "d3d9_common.h" #ifdef HAVE_D3DX -#ifdef _XBOX #include #include -#else -#include "../include/d3d9/d3dx9tex.h" -#endif - #endif #ifdef _XBOX From fec8d3294ce7b557343f77460c4003489c955bc3 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 17:23:14 +0200 Subject: [PATCH 432/517] Cleanup --- gfx/common/d3d8_common.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gfx/common/d3d8_common.c b/gfx/common/d3d8_common.c index 1b88aefa68..f05ab1a18a 100644 --- a/gfx/common/d3d8_common.c +++ b/gfx/common/d3d8_common.c @@ -29,12 +29,8 @@ #include "../../verbosity.h" #ifdef HAVE_D3DX -#ifdef _XBOX #include #include -#else -#include "../include/d3d8/d3dx8tex.h" -#endif #endif #include "d3d8_common.h" From 789ef63deed43fe035d6922893c0bf3c92dbdc52 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 17:35:36 +0200 Subject: [PATCH 433/517] (360) Don't bake in HAVE_XUI anymore; buildfixes for C++ --- gfx/common/d3d9_common.c | 3 +- gfx/common/d3d9_common.h | 4 +- gfx/drivers_font/xdk360_fonts.cpp | 59 ++++++++++++-------- menu/drivers/xui.cpp | 5 +- pkg/msvc/RetroArch-360/RetroArch-360.vcxproj | 12 ++-- 5 files changed, 48 insertions(+), 35 deletions(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index dfaff53ede..e8cd53abe3 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -606,8 +606,7 @@ void d3d9x_constant_table_set_matrix(LPDIRECT3DDEVICE9 dev, } const bool d3d9x_constant_table_set_float(void *p, - void *a, - const void *b, float val) + void *a, void *b, float val) { #if defined(HAVE_D3DX) LPDIRECT3DDEVICE9 dev = (LPDIRECT3DDEVICE9)a; diff --git a/gfx/common/d3d9_common.h b/gfx/common/d3d9_common.h index 91331033a6..17c2b05fec 100644 --- a/gfx/common/d3d9_common.h +++ b/gfx/common/d3d9_common.h @@ -245,7 +245,7 @@ static INLINE void d3d9_lock_rectangle_clear(void *tex, level = 0; #endif memset(lr->pBits, level, rectangle_height * lr->Pitch); - d3d9_unlock_rectangle(tex); + d3d9_unlock_rectangle((LPDIRECT3DTEXTURE9)tex); } static INLINE void d3d9_set_texture( @@ -668,7 +668,7 @@ void d3d9x_constant_table_set_matrix(LPDIRECT3DDEVICE9 dev, const void *d3d9x_get_buffer_ptr(void *data); const bool d3d9x_constant_table_set_float(void *p, - void *a, const void *b, float val); + void *a, void *b, float val); void *d3d9x_constant_table_get_constant_by_name(void *_tbl, void *_handle, void *_name); diff --git a/gfx/drivers_font/xdk360_fonts.cpp b/gfx/drivers_font/xdk360_fonts.cpp index 71da31402f..6d57e8520e 100644 --- a/gfx/drivers_font/xdk360_fonts.cpp +++ b/gfx/drivers_font/xdk360_fonts.cpp @@ -397,7 +397,7 @@ typedef struct float m_fFontBottomPadding; /* Padding below the strike zone. */ float m_fFontYAdvance; /* Number of pixels to move the cursor for a line feed. */ wchar_t * m_TranslatorTable; /* ASCII to glyph lookup table. */ - void *m_pFontTexture; + LPDIRECT3DTEXTURE9 m_pFontTexture; const GLYPH_ATTR* m_Glyphs; /* Array of glyphs. */ } xdk360_video_font_t; @@ -420,7 +420,7 @@ typedef struct static PackedResource m_xprResource; -static bool xdk360_video_font_create_shaders(xdk360_video_font_t * font, void *dev) +static bool xdk360_video_font_create_shaders(xdk360_video_font_t * font, LPDIRECT3DDEVICE9 dev) { ID3DXBuffer* pShaderCode = NULL; @@ -443,7 +443,7 @@ static bool xdk360_video_font_create_shaders(xdk360_video_font_t * font, void *d if (!d3d9_vertex_declaration_new(dev, decl, (void**)&font->s_FontLocals.m_pFontVertexDecl)) goto error; - if (!d3dx_compile_shader( font_hlsl_d3d9_program, sizeof(font_hlsl_d3d9_program)-1 , + if (!d3d9x_compile_shader( font_hlsl_d3d9_program, sizeof(font_hlsl_d3d9_program)-1 , NULL, NULL, "main_vertex", "vs.2.0", 0, &pShaderCode, NULL, NULL )) goto error; @@ -453,7 +453,7 @@ static bool xdk360_video_font_create_shaders(xdk360_video_font_t * font, void *d d3d9x_buffer_release(pShaderCode); - if (!d3dx_compile_shader(font_hlsl_d3d9_program, sizeof(font_hlsl_d3d9_program)-1 , + if (!d3d9x_compile_shader(font_hlsl_d3d9_program, sizeof(font_hlsl_d3d9_program)-1 , NULL, NULL, "main_fragment", "ps.2.0", 0,&pShaderCode, NULL, NULL )) goto error; @@ -468,8 +468,8 @@ static bool xdk360_video_font_create_shaders(xdk360_video_font_t * font, void *d error: if (pShaderCode) d3d9x_buffer_release(pShaderCode); - d3d9_free_pixel_shader(font->d3d->dev, font->s_FontLocals.m_pFontPixelShader); - d3d9_free_vertex_shader(font->d3d->dev, font->s_FontLocals.m_pFontVertexShader); + d3d9_free_pixel_shader((LPDIRECT3DDEVICE9)font->d3d->dev, font->s_FontLocals.m_pFontPixelShader); + d3d9_free_vertex_shader((LPDIRECT3DDEVICE9)font->d3d->dev, font->s_FontLocals.m_pFontVertexShader); d3d9_vertex_declaration_free(font->s_FontLocals.m_pFontVertexDecl); font->s_FontLocals.m_pFontPixelShader = NULL; font->s_FontLocals.m_pFontVertexShader = NULL; @@ -505,11 +505,11 @@ static void *xdk360_init_font(void *video_data, if (FAILED( m_xprResource.Create(font_path, 0, NULL))) goto error; - pFontTexture = m_xprResource.GetTexture( "FontTexture" ); + pFontTexture = (LPDIRECT3DTEXTURE9)m_xprResource.GetTexture( "FontTexture" ); pFontData = m_xprResource.GetData( "FontData"); /* Save a copy of the texture. */ - font->m_pFontTexture = pFontTexture; + font->m_pFontTexture = (LPDIRECT3DTEXTURE9)pFontTexture; /* Check version of file (to make sure it matches up with the FontMaker tool). */ pData = (const uint8_t*)pFontData; @@ -537,7 +537,7 @@ static void *xdk360_init_font(void *video_data, font->m_Glyphs = ((const FontFileStrikesImage_t *)pData)->m_Glyphs; /* Create the vertex and pixel shaders for rendering the font */ - if (!xdk360_video_font_create_shaders(font, font->d3d->dev)) + if (!xdk360_video_font_create_shaders(font, (LPDIRECT3DDEVICE9)font->d3d->dev)) { RARCH_ERR( "Could not create font shaders.\n" ); goto error; @@ -566,8 +566,8 @@ static void xdk360_free_font(void *data, bool is_threaded) font->m_cMaxGlyph = 0; font->m_TranslatorTable = NULL; - d3d9_free_pixel_shader(font->d3d->dev, font->s_FontLocals.m_pFontPixelShader); - d3d9_free_vertex_shader(font->d3d->dev, font->s_FontLocals.m_pFontVertexShader); + d3d9_free_pixel_shader((LPDIRECT3DDEVICE9)font->d3d->dev, font->s_FontLocals.m_pFontPixelShader); + d3d9_free_vertex_shader((LPDIRECT3DDEVICE9)font->d3d->dev, font->s_FontLocals.m_pFontVertexShader); d3d9_vertex_declaration_free(font->s_FontLocals.m_pFontVertexDecl); font->s_FontLocals.m_pFontPixelShader = NULL; @@ -583,24 +583,35 @@ static void xdk360_free_font(void *data, bool is_threaded) static void xdk360_render_msg_post(xdk360_video_font_t * font) { - if (!font || !font->d3d || !font->d3d->dev) + LPDIRECT3DDEVICE9 dev; + if (!font || !font->d3d) return; + dev = (LPDIRECT3DDEVICE9)font->d3d->dev; - d3d9_set_texture(font->d3d->dev, 0, NULL); - d3d9_set_vertex_declaration(font->d3d->dev, NULL); - d3d9_set_vertex_shader(font->d3d->dev, 0, NULL); - d3d9_set_pixel_shader(font->d3d->dev, NULL); - d3d9_set_render_state(font->d3d->dev, D3DRS_VIEWPORTENABLE, font->m_dwSavedState); + if (!dev) + return; + + d3d9_set_texture(dev, 0, NULL); + d3d9_set_vertex_declaration(dev, NULL); + d3d9_set_vertex_shader(dev, 0, NULL); + d3d9_set_pixel_shader(dev, NULL); + d3d9_set_render_state(dev, D3DRS_VIEWPORTENABLE, font->m_dwSavedState); } static void xdk360_render_msg_pre(xdk360_video_font_t * font) { float vTexScale[4]; D3DSURFACE_DESC TextureDesc; + LPDIRECT3DDEVICE9 dev; - if (!font || !font->d3d || !font->d3d->dev) + if (!font || !font->d3d) return; + dev = (LPDIRECT3DDEVICE9)font->d3d->dev; + + if (!dev) + return; + /* Save state. */ d3d9_get_render_state(font->d3d->dev, D3DRS_VIEWPORTENABLE, (DWORD*)&font->m_dwSavedState ); @@ -610,18 +621,18 @@ static void xdk360_render_msg_pre(xdk360_video_font_t * font) d3d9_texture_get_level_desc(font->m_pFontTexture, 0, &TextureDesc); /* Set render state. */ - d3d9_set_texture(font->d3d->dev, 0, font->m_pFontTexture); + d3d9_set_texture(dev, 0, font->m_pFontTexture); vTexScale[0] = 1.0f / TextureDesc.Width; vTexScale[1] = 1.0f / TextureDesc.Height; vTexScale[2] = 0.0f; vTexScale[3] = 0.0f; - d3d9_set_render_state(font->d3d->dev, D3DRS_VIEWPORTENABLE, FALSE); - d3d9_set_vertex_declaration(font->d3d->dev, font->s_FontLocals.m_pFontVertexDecl); - d3d9_set_vertex_shader(font->d3d->dev, 0, font->s_FontLocals.m_pFontVertexShader); - d3d9_set_pixel_shader(font->d3d->dev, font->s_FontLocals.m_pFontPixelShader); - d3d9_set_vertex_shader_constantf(font->d3d->dev, 2, vTexScale, 1); + d3d9_set_render_state(dev, D3DRS_VIEWPORTENABLE, FALSE); + d3d9_set_vertex_declaration(dev, font->s_FontLocals.m_pFontVertexDecl); + d3d9_set_vertex_shader(dev, 0, font->s_FontLocals.m_pFontVertexShader); + d3d9_set_pixel_shader(dev, font->s_FontLocals.m_pFontPixelShader); + d3d9_set_vertex_shader_constantf(dev, 2, vTexScale, 1); } static void xdk360_draw_text(xdk360_video_font_t *font, diff --git a/menu/drivers/xui.cpp b/menu/drivers/xui.cpp index 0d4c8767d0..837a4cb023 100644 --- a/menu/drivers/xui.cpp +++ b/menu/drivers/xui.cpp @@ -15,6 +15,8 @@ * If not, see . */ +#define CINTERFACE + #include #include #include @@ -47,6 +49,7 @@ #include "../../gfx/drivers/d3d.h" #include "../../gfx/common/d3d_common.h" +#include "../../gfx/common/d3d9_common.h" #define XUI_CONTROL_NAVIGATE_OK (XUI_CONTROL_NAVIGATE_RIGHT + 1) @@ -236,7 +239,7 @@ HRESULT XuiTextureLoader(IXuiDevice *pDevice, LPCWSTR szFileName, D3DX_DEFAULT_NONPOW2, 1, D3DUSAGE_CPU_CACHED_MEMORY, - (D3DFORMAT)d3d_get_argb8888_format(), + (D3DFORMAT)d3d9_get_argb8888_format(), D3DPOOL_DEFAULT, D3DX_FILTER_NONE, D3DX_FILTER_NONE, diff --git a/pkg/msvc/RetroArch-360/RetroArch-360.vcxproj b/pkg/msvc/RetroArch-360/RetroArch-360.vcxproj index 83399e9ae9..85bd70e68c 100644 --- a/pkg/msvc/RetroArch-360/RetroArch-360.vcxproj +++ b/pkg/msvc/RetroArch-360/RetroArch-360.vcxproj @@ -113,7 +113,7 @@ true false MultiThreadedDebug - _DEBUG;_XBOX;HAVE_XINPUT2;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_NETWORKING;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + _DEBUG;_XBOX;HAVE_XINPUT2;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_MENU;HAVE_NETWORKING;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN Callcap $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -152,7 +152,7 @@ AnalyzeOnly false MultiThreadedDebug - _DEBUG;_XBOX;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + _DEBUG;_XBOX;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_MENU;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN Callcap $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -192,7 +192,7 @@ Size false MultiThreaded - NDEBUG;_XBOX;PROFILE;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + NDEBUG;_XBOX;PROFILE;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_MENU;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN Callcap $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -237,7 +237,7 @@ Size false MultiThreaded - NDEBUG;_XBOX;PROFILE;FASTCAP;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XUI;HAVE_MENU;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + NDEBUG;_XBOX;PROFILE;FASTCAP;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_MENU;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -279,7 +279,7 @@ false false MultiThreaded - NDEBUG;_XBOX;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE=1;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XUI;HAVE_MENU;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + NDEBUG;_XBOX;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE=1;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_MENU;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) @@ -321,7 +321,7 @@ false false MultiThreaded - NDEBUG;_XBOX;LTCG;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_XUI;HAVE_MENU;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN + NDEBUG;_XBOX;LTCG;%(PreprocessorDefinitions);HAVE_XINPUT2;_CRT_SECURE_NO_WARNINGS;RARCH_CONSOLE;HAVE_MENU;HAVE_NETWORKING;HAVE_SOCKET_LEGACY;HAVE_ZLIB;HAVE_RARCH_EXEC;D3DCOMPILE_USEVOIDS;HAVE_RUNAHEAD;HAVE_GRIFFIN;HAVE_HLSL;HAVE_CC_RESAMPLER;HAVE_D3D9;HAVE_D3D;RARCH_INTERNAL;MSB_FIRST;_XBOX360;WANT_ZLIB;HAVE_XAUDIO;HAVE_RPNG;HAVE_RJPEG;HAVE_THREADS;HAVE_FILTERS_BUILTIN $(SolutionDir)\..\..\deps\zlib;$(SolutionDir)\..\..\libretro-common\include;$(SolutionDir)\..\..\deps;$(SolutionDir)\..\..\deps\stb;$(SolutionDir)\..\..\;%(AdditionalIncludeDirectories) From c90c98dcaa7dec7811e39cf1407361117acc10bc Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 17:37:37 +0200 Subject: [PATCH 434/517] Silence warnings --- gfx/drivers_shader/shader_hlsl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 1500f2941c..96d937043d 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -175,11 +175,11 @@ static void hlsl_set_params(void *dat, void *shader_data) if (program->frame_cnt_f) d3d9x_constant_table_set_float(program->f_ctable, - d3dr, program->frame_cnt_f, frame_cnt); + d3dr, (void*)program->frame_cnt_f, frame_cnt); if (program->frame_dir_f) d3d9x_constant_table_set_float(program->f_ctable, - d3dr, program->frame_dir_f, + d3dr, (void*)program->frame_dir_f, state_manager_frame_is_reversed() ? -1.0 : 1.0); if (program->vid_size_v) @@ -191,11 +191,11 @@ static void hlsl_set_params(void *dat, void *shader_data) if (program->frame_cnt_v) d3d9x_constant_table_set_float(program->v_ctable, - d3dr, program->frame_cnt_v, frame_cnt); + d3dr, (void*)program->frame_cnt_v, frame_cnt); if (program->frame_dir_v) d3d9x_constant_table_set_float(program->v_ctable, - d3dr, program->frame_dir_v, + d3dr, (void*)program->frame_dir_v, state_manager_frame_is_reversed() ? -1.0 : 1.0); /* TODO - set lookup textures/FBO textures/state parameters/etc */ From 77d815a5728e7a669557416c94a0845c3226cfcd Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 17:48:31 +0200 Subject: [PATCH 435/517] Get rid of warning --- gfx/common/d3d9_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/common/d3d9_common.c b/gfx/common/d3d9_common.c index e8cd53abe3..d71a4ceb18 100644 --- a/gfx/common/d3d9_common.c +++ b/gfx/common/d3d9_common.c @@ -563,7 +563,7 @@ void *d3d9x_constant_table_get_constant_by_name(void *_tbl, LPD3DXCONSTANTTABLE consttbl = (LPD3DXCONSTANTTABLE)_tbl; LPCSTR name = (LPCSTR)_name; if (consttbl && handle && name) - return consttbl->lpVtbl->GetConstantByName(consttbl, + return (void*)consttbl->lpVtbl->GetConstantByName(consttbl, handle, name); #endif return NULL; From d6c17178a3ca38f484baf403ac6768cb289b47a8 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 18:25:41 +0200 Subject: [PATCH 436/517] Add some logging for shader backend setup --- gfx/drivers_renderchain/d3d9_hlsl_renderchain.c | 7 ++----- gfx/drivers_shader/shader_hlsl.c | 11 +++++++++++ gfx/video_driver.c | 13 +++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c index 21bfcf7539..5944a6c799 100644 --- a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c +++ b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c @@ -266,7 +266,6 @@ static bool hlsl_d3d9_renderchain_init_shader(void *data, void *renderchain_data) { video_shader_ctx_init_t init; - bool ret = false; d3d_video_t *d3d = (d3d_video_t*)data; settings_t *settings = config_get_ptr(); (void)renderchain_data; @@ -277,13 +276,11 @@ static bool hlsl_d3d9_renderchain_init_shader(void *data, init.shader_type = RARCH_SHADER_HLSL; init.data = data; init.path = retroarch_get_shader_preset(); - init.shader = &hlsl_backend; + init.shader = NULL; RARCH_LOG("D3D]: Using HLSL shader backend.\n"); - ret = video_shader_driver_init(&init); - - return ret; + return video_shader_driver_init(&init); } static bool hlsl_d3d9_renderchain_init(void *data, diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index 96d937043d..c85bb956d6 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -234,12 +234,18 @@ static bool hlsl_compile_program( strlen(program_info->combined), NULL, NULL, "main_fragment", "ps_3_0", 0, &code_f, &listing_f, &program->f_ctable )) + { + RARCH_ERR("Failure building stock fragment shader..\n"); goto error; + } if (!d3d9x_compile_shader(program_info->combined, strlen(program_info->combined), NULL, NULL, "main_vertex", "vs_3_0", 0, &code_v, &listing_v, &program->v_ctable )) + { + RARCH_ERR("Failure building stock vertex shader..\n"); goto error; + } } d3d9_create_pixel_shader(d3dr, (const DWORD*)d3d9x_get_buffer_ptr(code_f), (void**)&program->fprg); @@ -471,10 +477,15 @@ static void *hlsl_init(void *data, const char *path) goto error; } + RARCH_LOG("Setting up program attributes...\n"); + RARCH_LOG("Shader passes: %d\n", hlsl->cg_shader->passes); + for(i = 1; i <= hlsl->cg_shader->passes; i++) hlsl_set_program_attributes(hlsl, i); + RARCH_LOG("Setting up vertex shader...\n"); d3d9_set_vertex_shader(hlsl->dev, 1, hlsl->prg[1].vprg); + RARCH_LOG("Setting up pixel shader...\n"); d3d9_set_pixel_shader(hlsl->dev, hlsl->prg[1].fprg); return hlsl; diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 3c041da9ff..99045e0f35 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -3400,6 +3400,13 @@ static const shader_backend_t *video_shader_set_backend(enum rarch_shader_type t return &gl_glsl_backend; #else break; +#endif + case RARCH_SHADER_HLSL: +#ifdef HAVE_HLSL + RARCH_LOG("[Shader driver]: Using HLSL shader backend.\n"); + return &hlsl_backend; +#else + break; #endif case RARCH_SHADER_NONE: default: @@ -3614,9 +3621,15 @@ bool video_shader_driver_init(video_shader_ctx_init_t *init) if (string_is_equal(settings->arrays.menu_driver, "xmb") && init->shader->init_menu_shaders) + { + RARCH_LOG("Setting up menu pipeline shaders for XMB ... \n"); init->shader->init_menu_shaders(tmp); + } current_shader_data = tmp; + + RARCH_LOG("Resetting shader to defaults ... \n"); + current_shader = (shader_backend_t*)init->shader; video_shader_driver_reset_to_defaults(); From ebfa4bdcdceb14389d74d8b42c4991b773ff6314 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 18:34:23 +0200 Subject: [PATCH 437/517] Implement two stub functions --- gfx/drivers_shader/shader_hlsl.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/gfx/drivers_shader/shader_hlsl.c b/gfx/drivers_shader/shader_hlsl.c index c85bb956d6..56717b2261 100644 --- a/gfx/drivers_shader/shader_hlsl.c +++ b/gfx/drivers_shader/shader_hlsl.c @@ -605,6 +605,21 @@ static struct video_shader *hlsl_get_current_shader(void *data) return NULL; } +static enum gfx_wrap_type hlsl_wrap_type(void *data, unsigned idx) +{ +#if 0 + /* TODO/FIXME - actual implementation */ +#endif + return RARCH_WRAP_BORDER; +} + +static bool hlsl_set_coords(void *handle_data, + void *shader_data, const struct video_coords *coords) +{ + /* TODO/FIXME - actual implementation */ + return false; +} + const shader_backend_t hlsl_backend = { hlsl_init, NULL, /* hlsl_init_menu_shaders */ @@ -615,9 +630,9 @@ const shader_backend_t hlsl_backend = { hlsl_use, hlsl_num, hlsl_filter_type, - NULL, /* hlsl_wrap_type */ + hlsl_wrap_type, hlsl_shader_scale, - NULL, /* hlsl_set_coords */ + hlsl_set_coords, hlsl_set_mvp, NULL, /* hlsl_get_prev_textures */ hlsl_get_feedback_pass, From a93289e311829e08668c821065dce3b17f0558da Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 23 Apr 2018 22:47:50 +0200 Subject: [PATCH 438/517] (materialui) add icons --- menu/drivers/materialui.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 26e1e1f9df..dc18cff33b 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -2482,13 +2482,15 @@ static void materialui_list_insert(void *userdata, else if ( string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TO_FAVORITES)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TO_FAVORITES_PLAYLIST)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_GOTO_FAVORITES)) ) { node->texture_switch2_index = MUI_TEXTURE_ADD_TO_FAVORITES; node->texture_switch2_set = true; } - else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RENAME_ENTRY))) + else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RENAME_ENTRY)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION))) { node->texture_switch2_index = MUI_TEXTURE_RENAME; node->texture_switch2_set = true; From 431d15746ea6676340e66084f819cd4f7fc5b0fa Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Mon, 23 Apr 2018 17:08:43 -0400 Subject: [PATCH 439/517] test: Disable Travis email notifications @twinaphex reported that he was getting a lot of Travis email notifications. These are not needed, as the build status is reported on both Pull Requests, and the Status Badge. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index e950b2c19b..c05e82b7bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,3 +64,5 @@ addons: build_command_prepend: "./configure; make clean" build_command: "make" branch_pattern: coverity_scan +notifications: + email: false From 27605860869a11c5d64f1051beb11ddc1740b47f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 04:46:13 +0200 Subject: [PATCH 440/517] Cleanups --- gfx/video_driver.c | 122 +++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 48 deletions(-) diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 99045e0f35..acd5eb8152 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -878,7 +878,9 @@ static bool video_driver_pixel_converter_init(unsigned size) if (hwr && hwr->context_type != RETRO_HW_CONTEXT_NONE) return true; - RARCH_WARN("0RGB1555 pixel format is deprecated, and will be slower. For 15/16-bit, RGB565 format is preferred.\n"); + RARCH_WARN("0RGB1555 pixel format is deprecated," + " and will be slower. For 15/16-bit, RGB565" + " format is preferred.\n"); scalr = (video_pixel_scaler_t*)calloc(1, sizeof(*scalr)); @@ -1046,7 +1048,8 @@ static bool video_driver_init_internal(bool *video_is_threaded) } else #endif - video_driver_data = current_video->init(&video, input_get_double_ptr(), + video_driver_data = current_video->init( + &video, input_get_double_ptr(), input_driver_get_data_ptr()); if (!video_driver_data) @@ -1437,7 +1440,8 @@ void video_driver_monitor_adjust_system_rates(void) if (timing_skew <= settings->floats.audio_max_timing_skew) return; - RARCH_LOG("[Video]: Timings deviate too much. Will not adjust. (Display = %.2f Hz, Game = %.2f Hz)\n", + RARCH_LOG("[Video]: Timings deviate too much. Will not adjust." + " (Display = %.2f Hz, Game = %.2f Hz)\n", video_refresh_rate, (float)info->fps); @@ -1549,7 +1553,8 @@ void video_driver_menu_settings(void **list_data, void *list_info_data, parent_group, general_write_handler, general_read_handler); - menu_settings_list_current_add_range(list, list_info, 0, 5, 1, true, true); + menu_settings_list_current_add_range(list, list_info, + 0, 5, 1, true, true); #endif #endif } @@ -1611,7 +1616,8 @@ bool video_driver_is_stub_frame(void) bool video_driver_supports_recording(void) { settings_t *settings = config_get_ptr(); - return settings->bools.video_gpu_record && current_video->read_viewport; + return settings->bools.video_gpu_record + && current_video->read_viewport; } bool video_driver_supports_viewport_read(void) @@ -1637,7 +1643,8 @@ void video_driver_set_viewport_config(void) { struct retro_game_geometry *geom = &video_driver_av_info.geometry; - if (geom->aspect_ratio > 0.0f && settings->bools.video_aspect_ratio_auto) + if (geom->aspect_ratio > 0.0f && + settings->bools.video_aspect_ratio_auto) aspectratio_lut[ASPECT_RATIO_CONFIG].value = geom->aspect_ratio; else { @@ -1864,7 +1871,8 @@ void video_driver_update_viewport(struct video_viewport* vp, bool force_full, bo } else if (device_aspect > desired_aspect) { - delta = (desired_aspect / device_aspect - 1.0f) / 2.0f + 0.5f; + delta = (desired_aspect / device_aspect - 1.0f) + / 2.0f + 0.5f; vp->x = (int)roundf(vp->full_width * (0.5f - delta)); vp->width = (unsigned)roundf(2.0f * vp->full_width * delta); vp->y = 0; @@ -1874,7 +1882,8 @@ void video_driver_update_viewport(struct video_viewport* vp, bool force_full, bo { vp->x = 0; vp->width = vp->full_width; - delta = (device_aspect / desired_aspect - 1.0f) / 2.0f + 0.5f; + delta = (device_aspect / desired_aspect - 1.0f) + / 2.0f + 0.5f; vp->y = (int)roundf(vp->full_height * (0.5f - delta)); vp->height = (unsigned)roundf(2.0f * vp->full_height * delta); } @@ -1981,18 +1990,17 @@ bool video_driver_find_driver(void) void video_driver_apply_state_changes(void) { - if (!video_driver_poke) - return; - if (video_driver_poke->apply_state_changes) + if (video_driver_poke && + video_driver_poke->apply_state_changes) video_driver_poke->apply_state_changes(video_driver_data); } bool video_driver_read_viewport(uint8_t *buffer, bool is_idle) { if ( current_video->read_viewport - && current_video->read_viewport(video_driver_data, buffer, is_idle)) + && current_video->read_viewport( + video_driver_data, buffer, is_idle)) return true; - return false; } @@ -2201,7 +2209,8 @@ void video_driver_gpu_record_deinit(void) video_driver_record_gpu_buffer = NULL; } -bool video_driver_get_current_software_framebuffer(struct retro_framebuffer *fb) +bool video_driver_get_current_software_framebuffer( + struct retro_framebuffer *fb) { if ( video_driver_poke @@ -2288,7 +2297,8 @@ void video_viewport_get_scaled_integer(struct video_viewport *vp, unsigned base_width; /* Use system reported sizes as these define the * geometry for the "normal" case. */ - unsigned base_height = video_driver_av_info.geometry.base_height; + unsigned base_height = + video_driver_av_info.geometry.base_height; if (base_height == 0) base_height = 1; @@ -2449,7 +2459,6 @@ void video_driver_frame(const void *data, unsigned width, if (video_info.fps_show) { if (video_info.framecount_show) - { snprintf( video_info.fps_text, sizeof(video_info.fps_text), @@ -2457,15 +2466,12 @@ void video_driver_frame(const void *data, unsigned width, last_fps, msg_hash_to_str(MSG_FRAMES), (uint64_t)video_driver_frame_count); - } else - { snprintf( video_info.fps_text, sizeof(video_info.fps_text), "FPS: %6.1f", last_fps); - } } } else @@ -2501,7 +2507,8 @@ void video_driver_frame(const void *data, unsigned width, || video_driver_record_gpu_buffer ) && recording_data ) - recording_dump_frame(data, width, height, pitch, video_info.runloop_is_idle); + recording_dump_frame(data, width, height, + pitch, video_info.runloop_is_idle); if (data && video_driver_state_filter && video_driver_frame_filter(data, &video_info, width, height, pitch, @@ -2549,7 +2556,8 @@ void video_driver_frame(const void *data, unsigned width, video_info.osd_stat_params.drop_y = -2; video_info.osd_stat_params.drop_mod = 0.3f; video_info.osd_stat_params.drop_alpha = 1.0f; - video_info.osd_stat_params.color = COLOR_ABGR(red, green, blue, alpha); + video_info.osd_stat_params.color = COLOR_ABGR( + red, green, blue, alpha); compute_audio_buffer_statistics(&audio_stats); @@ -2800,9 +2808,6 @@ void video_driver_build_info(video_frame_info_t *video_info) video_info->cb_set_mvp = video_driver_cb_shader_set_mvp; video_info->userdata = video_driver_get_ptr(false); -#if 0 - video_info->cb_set_coords = video_driver_cb_set_coords; -#endif #ifdef HAVE_THREADS video_driver_threaded_unlock(is_threaded); @@ -2841,12 +2846,14 @@ bool video_driver_translate_coord_viewport( return false; if (mouse_x >= 0 && mouse_x <= norm_full_vp_width) - scaled_screen_x = ((2 * mouse_x * 0x7fff) / norm_full_vp_width) - 0x7fff; + scaled_screen_x = ((2 * mouse_x * 0x7fff) + / norm_full_vp_width) - 0x7fff; else scaled_screen_x = -0x8000; /* OOB */ if (mouse_y >= 0 && mouse_y <= norm_full_vp_height) - scaled_screen_y = ((2 * mouse_y * 0x7fff) / norm_full_vp_height) - 0x7fff; + scaled_screen_y = ((2 * mouse_y * 0x7fff) + / norm_full_vp_height) - 0x7fff; else scaled_screen_y = -0x8000; /* OOB */ @@ -2854,12 +2861,14 @@ bool video_driver_translate_coord_viewport( mouse_y -= vp->y; if (mouse_x >= 0 && mouse_x <= norm_vp_width) - scaled_x = ((2 * mouse_x * 0x7fff) / norm_vp_width) - 0x7fff; + scaled_x = ((2 * mouse_x * 0x7fff) + / norm_vp_width) - 0x7fff; else scaled_x = -0x8000; /* OOB */ if (mouse_y >= 0 && mouse_y <= norm_vp_height) - scaled_y = ((2 * mouse_y * 0x7fff) / norm_vp_height) - 0x7fff; + scaled_y = ((2 * mouse_y * 0x7fff) + / norm_vp_height) - 0x7fff; else scaled_y = -0x8000; /* OOB */ @@ -2884,7 +2893,8 @@ void video_driver_get_status(uint64_t *frame_count, bool * is_alive, bool *is_focused) { *frame_count = video_driver_frame_count; - *is_alive = current_video ? current_video->alive(video_driver_data) : true; + *is_alive = current_video ? + current_video->alive(video_driver_data) : true; *is_focused = video_driver_cb_has_focus(); } @@ -2937,7 +2947,8 @@ bool video_context_driver_find_prev_driver(void) bool video_context_driver_find_next_driver(void) { settings_t *settings = config_get_ptr(); - int i = find_video_context_driver_index(settings->arrays.video_context_driver); + int i = find_video_context_driver_index( + settings->arrays.video_context_driver); if (i >= 0 && gfx_ctx_drivers[i + 1]) { @@ -2964,7 +2975,8 @@ bool video_context_driver_find_next_driver(void) * * Initialize graphics context driver. * - * Returns: graphics context driver if successfully initialized, otherwise NULL. + * Returns: graphics context driver if successfully initialized, + * otherwise NULL. **/ static const gfx_ctx_driver_t *video_context_driver_init( void *data, @@ -3085,7 +3097,8 @@ bool video_context_driver_init_image_buffer(const video_info_t *data) { if ( current_video_context.image_buffer_init - && current_video_context.image_buffer_init(video_context_data, data)) + && current_video_context.image_buffer_init( + video_context_data, data)) return true; return false; } @@ -3207,7 +3220,8 @@ bool video_context_driver_get_refresh_rate(float *refresh_rate) bool video_context_driver_input_driver(gfx_ctx_input_t *inp) { settings_t *settings = config_get_ptr(); - const char *joypad_name = settings ? settings->arrays.input_joypad_driver : NULL; + const char *joypad_name = settings ? + settings->arrays.input_joypad_driver : NULL; if (!current_video_context.input_driver) return false; @@ -3263,7 +3277,8 @@ bool video_context_driver_get_context_data(void *data) { if (!current_video_context.get_context_data) return false; - *(void**)data = current_video_context.get_context_data(video_context_data); + *(void**)data = current_video_context.get_context_data( + video_context_data); return true; } @@ -3295,7 +3310,7 @@ bool video_context_driver_get_flags(gfx_ctx_flags_t *flags) if (deferred_video_context_driver_set_flags) { - flags->flags = deferred_flag_data.flags; + flags->flags = deferred_flag_data.flags; deferred_video_context_driver_set_flags = false; return true; } @@ -3310,7 +3325,7 @@ bool video_context_driver_set_flags(gfx_ctx_flags_t *flags) return false; if (!current_video_context.set_flags) { - deferred_flag_data.flags = flags->flags; + deferred_flag_data.flags = flags->flags; deferred_video_context_driver_set_flags = true; return false; } @@ -3370,7 +3385,8 @@ bool video_driver_cached_frame_has_valid_framebuffer(void) return false; } -static const shader_backend_t *video_shader_set_backend(enum rarch_shader_type type) +static const shader_backend_t *video_shader_set_backend( + enum rarch_shader_type type) { switch (type) { @@ -3446,7 +3462,8 @@ void video_shader_driver_set_parameters(void *data) } } -bool video_shader_driver_get_prev_textures(video_shader_ctx_texture_t *texture) +bool video_shader_driver_get_prev_textures( + video_shader_ctx_texture_t *texture) { if (!texture || !current_shader) return false; @@ -3475,7 +3492,8 @@ bool video_shader_driver_get_current_shader(video_shader_ctx_t *shader) return true; } -bool video_shader_driver_direct_get_current_shader(video_shader_ctx_t *shader) +bool video_shader_driver_direct_get_current_shader( + video_shader_ctx_t *shader) { shader->data = current_shader->get_current_shader(current_shader_data); @@ -3491,7 +3509,7 @@ bool video_shader_driver_deinit(void) current_shader->deinit(current_shader_data); current_shader_data = NULL; - current_shader = NULL; + current_shader = NULL; return true; } @@ -3512,7 +3530,8 @@ static bool video_driver_cb_set_mvp(void *data, return true; } -static struct video_shader *video_shader_driver_get_current_shader_null(void *data) +static struct video_shader * +video_shader_driver_get_current_shader_null(void *data) { return NULL; } @@ -3529,13 +3548,15 @@ static void video_shader_driver_scale_null(void *data, (void)scale; } -static bool video_shader_driver_mipmap_input_null(void *data, unsigned idx) +static bool video_shader_driver_mipmap_input_null( + void *data, unsigned idx) { (void)idx; return false; } -static bool video_shader_driver_filter_type_null(void *data, unsigned idx, bool *smooth) +static bool video_shader_driver_filter_type_null( + void *data, unsigned idx, bool *smooth) { (void)idx; (void)smooth; @@ -3547,7 +3568,8 @@ static unsigned video_shader_driver_num_null(void *data) return 0; } -static bool video_shader_driver_get_feedback_pass_null(void *data, unsigned *idx) +static bool video_shader_driver_get_feedback_pass_null( + void *data, unsigned *idx) { (void)idx; return false; @@ -3696,12 +3718,14 @@ void video_driver_set_coords(video_shader_ctx_coords_t *coords) { if (current_shader && current_shader->set_coords) current_shader->set_coords(coords->handle_data, - current_shader_data, (const struct video_coords*)coords->data); + current_shader_data, + (const struct video_coords*)coords->data); else { if (video_driver_poke && video_driver_poke->set_coords) video_driver_poke->set_coords(coords->handle_data, - current_shader_data, (const struct video_coords*)coords->data); + current_shader_data, + (const struct video_coords*)coords->data); } } @@ -3711,11 +3735,13 @@ void video_driver_set_mvp(video_shader_ctx_mvp_t *mvp) return; if (current_shader && current_shader->set_mvp) - current_shader->set_mvp(mvp->data, current_shader_data, mvp->matrix); + current_shader->set_mvp(mvp->data, + current_shader_data, mvp->matrix); else { if (video_driver_poke && video_driver_poke->set_mvp) - video_driver_poke->set_mvp(mvp->data, current_shader_data, mvp->matrix); + video_driver_poke->set_mvp(mvp->data, + current_shader_data, mvp->matrix); } } From a482c90222c637e98a85dd27f2ccb3b77c5bebf2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 04:49:02 +0200 Subject: [PATCH 441/517] (dynamic.c) Cleanups --- dynamic.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dynamic.c b/dynamic.c index 4795c22011..bed42e2370 100644 --- a/dynamic.c +++ b/dynamic.c @@ -1759,22 +1759,14 @@ bool rarch_environment_cb(unsigned cmd, void *data) { int result = 0; if (!audio_driver_is_suspended() && audio_driver_is_active()) - { result |= 2; - } if (video_driver_is_active() && !video_driver_is_stub_frame()) - { result |= 1; - } #ifdef HAVE_RUNAHEAD if (want_fast_savestate()) - { result |= 4; - } if (get_hard_disable_audio()) - { result |= 8; - } #endif if (data != NULL) { From 72428a23c5c55f812be1e63f235fc7e7623425dc Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 07:07:58 +0200 Subject: [PATCH 442/517] (shader_vulkan.cpp) Move some functions to vulkan_common.c --- gfx/common/vulkan_common.c | 28 ++++++++ gfx/common/vulkan_common.h | 6 ++ gfx/drivers_shader/shader_vulkan.cpp | 101 ++++++--------------------- 3 files changed, 57 insertions(+), 78 deletions(-) diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 5ea05e04da..c1231e780b 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -962,6 +962,34 @@ void vulkan_image_layout_transition( 1, &barrier); } +void vulkan_image_layout_transition_levels( + VkCommandBuffer cmd, VkImage image, uint32_t levels, + VkImageLayout old_layout, VkImageLayout new_layout, + VkAccessFlags src_access, VkAccessFlags dst_access, + VkPipelineStageFlags src_stages, VkPipelineStageFlags dst_stages) +{ + VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + + barrier.srcAccessMask = src_access; + barrier.dstAccessMask = dst_access; + barrier.oldLayout = old_layout; + barrier.newLayout = new_layout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = levels; + barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + + vkCmdPipelineBarrier(cmd, + src_stages, + dst_stages, + false, + 0, NULL, + 0, NULL, + 1, &barrier); +} + struct vk_buffer vulkan_create_buffer( const struct vulkan_context *context, size_t size, VkBufferUsageFlags usage) diff --git a/gfx/common/vulkan_common.h b/gfx/common/vulkan_common.h index e4714142ef..d154d77ac7 100644 --- a/gfx/common/vulkan_common.h +++ b/gfx/common/vulkan_common.h @@ -446,6 +446,12 @@ void vulkan_image_layout_transition(vk_t *vk, VkAccessFlags srcAccess, VkAccessFlags dstAccess, VkPipelineStageFlags srcStages, VkPipelineStageFlags dstStages); +void vulkan_image_layout_transition_levels( + VkCommandBuffer cmd, VkImage image, uint32_t levels, + VkImageLayout old_layout, VkImageLayout new_layout, + VkAccessFlags src_access, VkAccessFlags dst_access, + VkPipelineStageFlags src_stages, VkPipelineStageFlags dst_stages); + static INLINE unsigned vulkan_format_to_bpp(VkFormat format) { switch (format) diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp index de864eaf61..57559d3258 100644 --- a/gfx/drivers_shader/shader_vulkan.cpp +++ b/gfx/drivers_shader/shader_vulkan.cpp @@ -56,62 +56,6 @@ static unsigned num_miplevels(unsigned width, unsigned height) return levels; } -static void image_layout_transition_levels( - VkCommandBuffer cmd, VkImage image, uint32_t levels, - VkImageLayout old_layout, VkImageLayout new_layout, - VkAccessFlags src_access, VkAccessFlags dst_access, - VkPipelineStageFlags src_stages, VkPipelineStageFlags dst_stages) -{ - VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - - barrier.srcAccessMask = src_access; - barrier.dstAccessMask = dst_access; - barrier.oldLayout = old_layout; - barrier.newLayout = new_layout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.levelCount = levels; - barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; - - vkCmdPipelineBarrier(cmd, - src_stages, - dst_stages, - false, - 0, nullptr, - 0, nullptr, - 1, &barrier); -} - -static void image_layout_transition( - VkCommandBuffer cmd, VkImage image, - VkImageLayout old_layout, VkImageLayout new_layout, - VkAccessFlags src_access, VkAccessFlags dst_access, - VkPipelineStageFlags src_stages, VkPipelineStageFlags dst_stages) -{ - image_layout_transition_levels(cmd, image, VK_REMAINING_MIP_LEVELS, - old_layout, new_layout, - src_access, dst_access, - src_stages, dst_stages); -} - -static uint32_t find_memory_type( - const VkPhysicalDeviceMemoryProperties &mem_props, - uint32_t device_reqs, uint32_t host_reqs) -{ - uint32_t i; - for (i = 0; i < VK_MAX_MEMORY_TYPES; i++) - { - if ((device_reqs & (1u << i)) && - (mem_props.memoryTypes[i].propertyFlags & host_reqs) == host_reqs) - return i; - } - - RARCH_ERR("[Vulkan]: Failed to find valid memory type. This should never happen."); - abort(); -} - static uint32_t find_memory_type_fallback( const VkPhysicalDeviceMemoryProperties &mem_props, uint32_t device_reqs, uint32_t host_reqs) @@ -124,7 +68,7 @@ static uint32_t find_memory_type_fallback( return i; } - return find_memory_type(mem_props, device_reqs, 0); + return vulkan_find_memory_type(&mem_props, device_reqs, 0); } static void build_identity_matrix(float *data) @@ -1048,8 +992,8 @@ void vulkan_filter_chain::update_history(DeferredDisposer &disposer, VkCommandBu // Transition input texture to something appropriate. if (input_texture.layout != VK_IMAGE_LAYOUT_GENERAL) { - image_layout_transition(cmd, - input_texture.image, + vulkan_image_layout_transition_levels(cmd, + input_texture.image,VK_REMAINING_MIP_LEVELS, input_texture.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 0, @@ -1075,8 +1019,8 @@ void vulkan_filter_chain::update_history(DeferredDisposer &disposer, VkCommandBu // Transition input texture back. if (input_texture.layout != VK_IMAGE_LAYOUT_GENERAL) { - image_layout_transition(cmd, - input_texture.image, + vulkan_image_layout_transition_levels(cmd, + input_texture.image,VK_REMAINING_MIP_LEVELS, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, input_texture.layout, 0, @@ -1205,8 +1149,8 @@ Buffer::Buffer(VkDevice device, VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; alloc.allocationSize = mem_reqs.size; - alloc.memoryTypeIndex = find_memory_type( - mem_props, mem_reqs.memoryTypeBits, + alloc.memoryTypeIndex = vulkan_find_memory_type( + &mem_props, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); @@ -2056,8 +2000,8 @@ void Pass::build_commands( // the passes that end up on-screen. if (!final_pass) { - // Render. - image_layout_transition_levels(cmd, + /* Render. */ + vulkan_image_layout_transition_levels(cmd, framebuffer->get_image(), 1, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, @@ -2140,9 +2084,9 @@ void Pass::build_commands( else { // Barrier to sync with next pass. - image_layout_transition( + vulkan_image_layout_transition_levels( cmd, - framebuffer->get_image(), + framebuffer->get_image(),VK_REMAINING_MIP_LEVELS, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, @@ -2175,7 +2119,7 @@ void Framebuffer::clear(VkCommandBuffer cmd) VkClearColorValue color; VkImageSubresourceRange range; - image_layout_transition(cmd, image, + vulkan_image_layout_transition_levels(cmd, image,VK_REMAINING_MIP_LEVELS, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, @@ -2191,7 +2135,7 @@ void Framebuffer::clear(VkCommandBuffer cmd) vkCmdClearColorImage(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range); - image_layout_transition(cmd, image, + vulkan_image_layout_transition_levels(cmd, image,VK_REMAINING_MIP_LEVELS, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, @@ -2330,7 +2274,7 @@ void Framebuffer::copy(VkCommandBuffer cmd, { VkImageCopy region; - image_layout_transition(cmd, image, + vulkan_image_layout_transition_levels(cmd, image,VK_REMAINING_MIP_LEVELS, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, @@ -2350,7 +2294,7 @@ void Framebuffer::copy(VkCommandBuffer cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - image_layout_transition(cmd, image, + vulkan_image_layout_transition_levels(cmd, image,VK_REMAINING_MIP_LEVELS, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, @@ -2688,9 +2632,9 @@ static unique_ptr vulkan_filter_chain_load_lut(VkCommandBuffer cm image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; vkCreateImage(info->device, &image_info, nullptr, &tex); vkGetImageMemoryRequirements(info->device, tex, &mem_reqs); - alloc.allocationSize = mem_reqs.size; - alloc.memoryTypeIndex = find_memory_type( - *info->memory_properties, + alloc.allocationSize = mem_reqs.size; + alloc.memoryTypeIndex = vulkan_find_memory_type( + &*info->memory_properties, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); @@ -2717,7 +2661,7 @@ static unique_ptr vulkan_filter_chain_load_lut(VkCommandBuffer cm memcpy(ptr, image.pixels, image.width * image.height * sizeof(uint32_t)); buffer->unmap(); - image_layout_transition(cmd, tex, + vulkan_image_layout_transition_levels(cmd, tex,VK_REMAINING_MIP_LEVELS, VK_IMAGE_LAYOUT_UNDEFINED, shader->mipmap ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, @@ -2758,7 +2702,7 @@ static unique_ptr vulkan_filter_chain_load_lut(VkCommandBuffer cm /* Only injects execution and memory barriers, * not actual transition. */ - image_layout_transition(cmd, tex, + vulkan_image_layout_transition_levels(cmd, tex, VK_REMAINING_MIP_LEVELS, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_TRANSFER_WRITE_BIT, @@ -2772,7 +2716,7 @@ static unique_ptr vulkan_filter_chain_load_lut(VkCommandBuffer cm 1, &blit_region, VK_FILTER_LINEAR); } - image_layout_transition(cmd, tex, + vulkan_image_layout_transition_levels(cmd, tex,VK_REMAINING_MIP_LEVELS, shader->mipmap ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, @@ -3011,7 +2955,8 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( pass_info.rt_format = tmpinfo.swapchain.format; if (explicit_format) - RARCH_WARN("[slang]: Using explicit format for last pass in chain, but it is not rendered to framebuffer, using swapchain format instead.\n"); + RARCH_WARN("[slang]: Using explicit format for last pass in chain," + " but it is not rendered to framebuffer, using swapchain format instead.\n"); } else { From af351932bd4d0cfe647502bdcc8bb1c676868641 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 07:19:29 +0200 Subject: [PATCH 443/517] (XMB) Cleanups --- menu/drivers/xmb.c | 51 ++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 217eb5b6b5..134da0c448 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3709,20 +3709,6 @@ static void xmb_layout_ps3(xmb_handle_t *xmb, int width) xmb->icon_size = 128.0 * scale_factor; xmb->font_size = new_font_size; -#ifdef XMB_DEBUG - RARCH_LOG("[XMB] margin screen left: %.2f\n", xmb->margins_screen_left); - RARCH_LOG("[XMB] margin screen top: %.2f\n", xmb->margins_screen_top); - RARCH_LOG("[XMB] margin title left: %.2f\n", xmb->margins_title_left); - RARCH_LOG("[XMB] margin title top: %.2f\n", xmb->margins_title_top); - RARCH_LOG("[XMB] margin title bott: %.2f\n", xmb->margins_title_bottom); - RARCH_LOG("[XMB] margin label left: %.2f\n", xmb->margins_label_left); - RARCH_LOG("[XMB] margin label top: %.2f\n", xmb->margins_label_top); - RARCH_LOG("[XMB] margin sett left: %.2f\n", xmb->margins_setting_left); - RARCH_LOG("[XMB] icon spacing hor: %.2f\n", xmb->icon_spacing_horizontal); - RARCH_LOG("[XMB] icon spacing ver: %.2f\n", xmb->icon_spacing_vertical); - RARCH_LOG("[XMB] icon size: %.2f\n", xmb->icon_size); -#endif - menu_display_set_header_height(new_header_height); } @@ -3779,6 +3765,26 @@ static void xmb_layout_psp(xmb_handle_t *xmb, int width) xmb->icon_size = 128.0 * scale_factor; xmb->font_size = new_font_size; + menu_display_set_header_height(new_header_height); +} + +static void xmb_layout(xmb_handle_t *xmb) +{ + unsigned width, height, i, current, end; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + size_t selection = menu_navigation_get_selection(); + bool use_ps3_layout = false; + + video_driver_get_size(&width, &height); + + use_ps3_layout = width > 320 && height > 240; + + /* Mimic the layout of the PSP instead of the PS3 on tiny screens */ + if (use_ps3_layout) + xmb_layout_ps3(xmb, width); + else + xmb_layout_psp(xmb, width); + #ifdef XMB_DEBUG RARCH_LOG("[XMB] margin screen left: %.2f\n", xmb->margins_screen_left); RARCH_LOG("[XMB] margin screen top: %.2f\n", xmb->margins_screen_top); @@ -3793,23 +3799,6 @@ static void xmb_layout_psp(xmb_handle_t *xmb, int width) RARCH_LOG("[XMB] icon size: %.2f\n", xmb->icon_size); #endif - menu_display_set_header_height(new_header_height); -} - -static void xmb_layout(xmb_handle_t *xmb) -{ - unsigned width, height, i, current, end; - file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); - size_t selection = menu_navigation_get_selection(); - - video_driver_get_size(&width, &height); - - /* Mimic the layout of the PSP instead of the PS3 on tiny screens */ - if (width > 320 && height > 240) - xmb_layout_ps3(xmb, width); - else - xmb_layout_psp(xmb, width); - current = (unsigned)selection; end = (unsigned)menu_entries_get_size(); From 265ae728ddfb8c5aead4b3e33f49d1ea0b2d34a7 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 07:38:53 +0200 Subject: [PATCH 444/517] (XMB) Add XMB menu layout - ability to switch between PS3 (Console) and PSP (Handheld) layouts --- config.def.h | 1 + configuration.c | 1 + configuration.h | 1 + intl/msg_hash_ar.h | 4 ++++ intl/msg_hash_chs.h | 4 ++++ intl/msg_hash_cht.h | 4 ++++ intl/msg_hash_de.h | 4 ++++ intl/msg_hash_eo.h | 4 ++++ intl/msg_hash_es.h | 4 ++++ intl/msg_hash_fr.h | 4 ++++ intl/msg_hash_it.h | 4 ++++ intl/msg_hash_ja.h | 4 ++++ intl/msg_hash_ko.h | 4 ++++ intl/msg_hash_lbl.h | 2 ++ intl/msg_hash_nl.h | 4 ++++ intl/msg_hash_pl.h | 4 ++++ intl/msg_hash_pt_br.h | 4 ++++ intl/msg_hash_pt_pt.h | 4 ++++ intl/msg_hash_ru.h | 4 ++++ intl/msg_hash_us.h | 4 ++++ intl/msg_hash_vn.h | 4 ++++ menu/cbs/menu_cbs_get_value.c | 35 +++++++++++++++++++++++++++++++++++ menu/cbs/menu_cbs_sublabel.c | 4 ++++ menu/drivers/xmb.c | 31 ++++++++++++++++++++++++------- menu/menu_displaylist.c | 3 +++ menu/menu_setting.c | 14 ++++++++++++++ msg_hash.h | 1 + 27 files changed, 154 insertions(+), 7 deletions(-) diff --git a/config.def.h b/config.def.h index 3d4a6ab152..3d4a78e496 100644 --- a/config.def.h +++ b/config.def.h @@ -305,6 +305,7 @@ static unsigned xmb_alpha_factor = 75; static unsigned menu_font_color_red = 255; static unsigned menu_font_color_green = 255; static unsigned menu_font_color_blue = 255; +static unsigned xmb_layout = 0; static unsigned xmb_icon_theme = XMB_ICON_THEME_MONOCHROME; static unsigned xmb_theme = XMB_THEME_ELECTRIC_BLUE; #if defined(HAVE_LAKKA) || defined(__arm__) || defined(__PPC64__) || defined(__ppc64__) || defined(__powerpc64__) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__) diff --git a/configuration.c b/configuration.c index a03257b708..16b1c44ba9 100644 --- a/configuration.c +++ b/configuration.c @@ -1474,6 +1474,7 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("menu_left_thumbnails", &settings->uints.menu_left_thumbnails, true, menu_left_thumbnails_default, false); SETTING_UINT("xmb_alpha_factor", &settings->uints.menu_xmb_alpha_factor, true, xmb_alpha_factor, false); SETTING_UINT("xmb_scale_factor", &settings->uints.menu_xmb_scale_factor, true, xmb_scale_factor, false); + SETTING_UINT("xmb_layout", &settings->uints.menu_xmb_layout, true, xmb_layout, false); SETTING_UINT("xmb_theme", &settings->uints.menu_xmb_theme, true, xmb_icon_theme, false); SETTING_UINT("xmb_menu_color_theme", &settings->uints.menu_xmb_color_theme, true, xmb_theme, false); SETTING_UINT("menu_font_color_red", &settings->uints.menu_font_color_red, true, menu_font_color_red, false); diff --git a/configuration.h b/configuration.h index d4aeaa9f6d..890973086c 100644 --- a/configuration.h +++ b/configuration.h @@ -359,6 +359,7 @@ typedef struct settings unsigned menu_entry_normal_color; unsigned menu_entry_hover_color; unsigned menu_title_color; + unsigned menu_xmb_layout; unsigned menu_xmb_shader_pipeline; unsigned menu_xmb_scale_factor; unsigned menu_xmb_alpha_factor; diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index 1561f02d06..ac772516dc 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -1893,6 +1893,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Show Video Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Show Netplay Tab") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Menu Icon Theme") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -3053,6 +3055,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Select a disk image to insert.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Makes sure the framerate is capped while inside the menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Select a different theme for the icon. Changes will take effect after you restart the program.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index ef35928e1c..6e6f0155a8 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -1758,6 +1758,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS, "显示设置页") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "显示视频页") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "菜单图标主题") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2881,6 +2883,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Select a disk image to insert.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Makes sure the framerate is capped while inside the menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Select a different theme for the icon. Changes will take effect after you restart the program.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index 7f9945dabd..695095e7bd 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -1750,6 +1750,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS, "顯示設定頁") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "顯示視訊頁") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "選單圖標主題") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2873,6 +2875,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Select a disk image to insert.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Makes sure the framerate is capped while inside the menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Select a different theme for the icon. Changes will take effect after you restart the program.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_de.h b/intl/msg_hash_de.h index bf0784201b..1c7c9afee0 100644 --- a/intl/msg_hash_de.h +++ b/intl/msg_hash_de.h @@ -1811,6 +1811,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Zeige Tab 'Video'") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Zeige Tab 'Netplay'") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Menü-Design") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2963,6 +2965,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Wähle ein Datenträger-Abbild, das eingelegt werden soll.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Stelle sicher, dass die Bildwiederholrate im Menü begrenzt wird.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Wähle ein anderes Thema für das Menü aus. Änderungen werden übernommen, nachdem Du das Programm neu gestartet hast.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 227c42163f..b2e52d50ee 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -1654,6 +1654,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS, "Display Settings Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Display Video Tab") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Menu Icon Theme") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2744,6 +2746,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Select a disk image to insert.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Makes sure the framerate is capped while inside the menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Select a different theme for the icon. Changes will take effect after you restart the program.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_es.h b/intl/msg_hash_es.h index e8d8434dc9..637a8179ec 100644 --- a/intl/msg_hash_es.h +++ b/intl/msg_hash_es.h @@ -3233,6 +3233,8 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Mostrar pestaña juego en red" ) +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH( MENU_ENUM_LABEL_VALUE_XMB_THEME, "Tema de iconos del menú" @@ -5125,6 +5127,8 @@ MSG_HASH( MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Limita los FPS en el menú" ) +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH( MENU_ENUM_SUBLABEL_XMB_THEME, "Seleccionar un tema de iconos diferente. Los cambios tendrán efecto al reiniciar" diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index 0133bdec20..12c5f97c06 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -1775,6 +1775,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS, "Afficher l'onglet Paramètres") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Afficher l'onglet Vidéo") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Thème XMB") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2911,6 +2913,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Select a disk image to insert.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Makes sure the framerate is capped while inside the menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Select a different theme for the icon. Changes will take effect after you restart the program.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index d441d6b7e3..fcf6b070bf 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -1809,6 +1809,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Visualizza colonna Video") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Visualizza colonna Stanze Netplay ") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Icone del Menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2961,6 +2963,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Seleziona un'immagine disco da inserire") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Assicura che i fotogrammi siano attivi all'interno del menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Seleziona un tema diverso per l'icona. Le modifiche avranno effetto dopo il riavvio del programma.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index caa25ad2c3..3562981f0b 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -1877,6 +1877,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "ビデオタブを表示") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "ネットプレイタブを表示") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "メニューのアイコンテーマ") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2985,6 +2987,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "ディスクイメージを追加する。") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Makes sure the framerate is capped while inside the menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Select a different theme for the icon. Changes will take effect after you restart the program.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_ko.h b/intl/msg_hash_ko.h index 8c148efd8f..875cbbf201 100644 --- a/intl/msg_hash_ko.h +++ b/intl/msg_hash_ko.h @@ -1755,6 +1755,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "비디오 탭 보이기") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "넷플레이 탭 보이기") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "메뉴 아이콘 테마") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2875,6 +2877,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "삽입할 디스크 이미지를 선택하십시오.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "메뉴상에 있을 시에는 프레임 제한을 설정.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "다른 아이콘 테마 선택. 변경 내용은 프로그램을 다시 시작 후 적용됩니다.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 7942fd7b3e..cd2485fafa 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1269,6 +1269,8 @@ MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_VIDEO, "content_show_video") MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_NETPLAY, "content_show_netplay") +MSG_HASH(MENU_ENUM_LABEL_XMB_LAYOUT, + "xmb_layout") MSG_HASH(MENU_ENUM_LABEL_XMB_THEME, "xmb_theme") MSG_HASH(MSG_BRINGING_UP_COMMAND_INTERFACE_ON_PORT, diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index eeb0cb534d..3501262f53 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -1652,6 +1652,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS, "Instellingentab weergeven") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Videotab weergeven") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Menu Icon Theme") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2748,6 +2750,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Select a disk image to insert.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Makes sure the framerate is capped while inside the menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Select a different theme for the icon. Changes will take effect after you restart the program.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index f3624789ca..b43111f30e 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -1897,6 +1897,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Pokaż kartę Wideo") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Pokaż kartę Gry Online") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Motyw ikon menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -3063,6 +3065,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Wybierz obraz dysku, który chcesz wstawić.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Upewnia się, że liczba klatek na sekundę jest ograniczona w menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Wybierz inny motyw dla ikony. Zmiany zaczną obowiązywać po ponownym uruchomieniu programu.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index a81b17c9bf..f819763186 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -2395,6 +2395,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Exibir Aba de Netplay" ) +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Tema de Ícones do Menu" ) @@ -3822,6 +3824,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Certifica-se de que a taxa de quadros é controlada enquanto estiver dentro do menu." ) +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Selecionar um tema diferente para os ícones. As alterações terão efeito após reiniciar o programa." ) diff --git a/intl/msg_hash_pt_pt.h b/intl/msg_hash_pt_pt.h index 842f2b9bf1..ca37f48512 100644 --- a/intl/msg_hash_pt_pt.h +++ b/intl/msg_hash_pt_pt.h @@ -1745,6 +1745,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS, "Mostrar separador de definições") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Mostrar separador de vídeo") +MSG_HASH(MENU_ENUM_LABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Ícone do tema do menu") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2852,6 +2854,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Selecione uma imagem de disco para inserir.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Certifique-se de que a taxa de fotogramas atingida enquanto estiver dentro do menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Selecionar um tema diferente para este ícone. As alterações terão efeito após o reinício do programa.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_ru.h b/intl/msg_hash_ru.h index 5278ff7c4a..9f045d4b3d 100644 --- a/intl/msg_hash_ru.h +++ b/intl/msg_hash_ru.h @@ -1788,6 +1788,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Показать вкладку Видео") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Показать вкладку Сетевая игра") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Тема значка меню") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2926,6 +2928,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Выберите образ диска для загрузки.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Убедитесь, что частота кадров ограничена внутри меню.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Выберите другую тему для значка. Изменения заработают после перезагрузки.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 1c3ea5e0ee..02a1eee6b4 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1911,6 +1911,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Show Video Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Show Netplay Tab") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Menu Icon Theme") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -3098,6 +3100,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Select a disk image to insert.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Makes sure the framerate is capped while inside the menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Select a different theme for the icon. Changes will take effect after you restart the program.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index d3cc3bee25..971c4408cd 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -1773,6 +1773,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_VIDEO, "Display Video Tab") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_NETPLAY, "Display Netplay Tab") +MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + "Menu Layout") MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_THEME, "Menu Icon Theme") MSG_HASH(MENU_ENUM_LABEL_VALUE_YES, @@ -2909,6 +2911,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, "Select a disk image to insert.") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Makes sure the framerate is capped while inside the menu.") +MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, + "Select a different layout for the XMB interface.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Select a different theme for the icon. Changes will take effect after you restart the program.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 6ccfb0f31a..45827cc6da 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -922,6 +922,37 @@ static void menu_action_setting_disp_set_label_wifi_is_online( strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ONLINE), len); } +static void menu_action_setting_disp_set_label_xmb_layout( + file_list_t* list, + unsigned *w, unsigned type, unsigned i, + const char *label, + char *s, size_t len, + const char *entry_label, + const char *path, + char *s2, size_t len2) +{ + settings_t *settings = config_get_ptr(); + + strlcpy(s2, path, len2); + *w = 19; + + if (!settings) + return; + + switch (settings->uints.menu_xmb_layout) + { + case 0: + strlcpy(s, "Auto", len); + break; + case 1: + strlcpy(s, "Console", len); + break; + case 2: + strlcpy(s, "Handheld", len); + break; + } +} + static void menu_action_setting_disp_set_label_xmb_menu_color_theme( file_list_t* list, unsigned *w, unsigned type, unsigned i, @@ -1915,6 +1946,10 @@ static int menu_cbs_init_bind_get_string_representation_compare_label( BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_poll_type_behavior); break; + case MENU_ENUM_LABEL_XMB_LAYOUT: + BIND_ACTION_GET_VALUE(cbs, + menu_action_setting_disp_set_label_xmb_layout); + break; case MENU_ENUM_LABEL_XMB_THEME: BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_xmb_theme); diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 7be57bf835..c75c117a9f 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -298,6 +298,7 @@ default_sublabel_macro(action_bind_sublabel_disk_image_append, default_sublabel_macro(action_bind_sublabel_disk_index, MENU_ENUM_SUBLABEL_DISK_INDEX) default_sublabel_macro(action_bind_sublabel_disk_options, MENU_ENUM_SUBLABEL_DISK_OPTIONS) default_sublabel_macro(action_bind_sublabel_menu_throttle_framerate, MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE) +default_sublabel_macro(action_bind_sublabel_xmb_layout, MENU_ENUM_SUBLABEL_XMB_LAYOUT) default_sublabel_macro(action_bind_sublabel_xmb_icon_theme, MENU_ENUM_SUBLABEL_XMB_THEME) default_sublabel_macro(action_bind_sublabel_xmb_shadows_enable, MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE) default_sublabel_macro(action_bind_sublabel_xmb_vertical_thumbnails, MENU_ENUM_SUBLABEL_XMB_VERTICAL_THUMBNAILS) @@ -833,6 +834,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_XMB_VERTICAL_THUMBNAILS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_xmb_vertical_thumbnails); break; + case MENU_ENUM_LABEL_XMB_LAYOUT: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_xmb_layout); + break; case MENU_ENUM_LABEL_XMB_THEME: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_xmb_icon_theme); break; diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 134da0c448..937b1fd2e2 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3771,19 +3771,36 @@ static void xmb_layout_psp(xmb_handle_t *xmb, int width) static void xmb_layout(xmb_handle_t *xmb) { unsigned width, height, i, current, end; + settings_t *settings = config_get_ptr(); file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); size_t selection = menu_navigation_get_selection(); - bool use_ps3_layout = false; video_driver_get_size(&width, &height); - use_ps3_layout = width > 320 && height > 240; + switch (settings->uints.menu_xmb_layout) + { + /* Automatic */ + case 0: + { + bool use_ps3_layout = false; + use_ps3_layout = width > 320 && height > 240; - /* Mimic the layout of the PSP instead of the PS3 on tiny screens */ - if (use_ps3_layout) - xmb_layout_ps3(xmb, width); - else - xmb_layout_psp(xmb, width); + /* Mimic the layout of the PSP instead of the PS3 on tiny screens */ + if (use_ps3_layout) + xmb_layout_ps3(xmb, width); + else + xmb_layout_psp(xmb, width); + } + break; + /* PS3 */ + case 1: + xmb_layout_ps3(xmb, width); + break; + /* PSP */ + case 2: + xmb_layout_psp(xmb, width); + break; + } #ifdef XMB_DEBUG RARCH_LOG("[XMB] margin screen left: %.2f\n", xmb->margins_screen_left); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index ad81b13855..0a8e01f39c 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5356,6 +5356,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_FONT_COLOR_BLUE, PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_LAYOUT, + PARSE_ONLY_UINT, false); menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_XMB_THEME, PARSE_ONLY_UINT, false); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index a17aaeea15..79651c3743 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5457,6 +5457,20 @@ static bool setting_append_list( menu_settings_list_current_add_range(list, list_info, 0, 255, 1, true, true); settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED); + CONFIG_UINT( + list, list_info, + &settings->uints.menu_xmb_layout, + MENU_ENUM_LABEL_XMB_LAYOUT, + MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, + xmb_layout, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + menu_settings_list_current_add_range(list, list_info, 0, 2, 1, true, true); + menu_settings_list_current_add_cmd(list, list_info, CMD_EVENT_REINIT); + CONFIG_UINT( list, list_info, &settings->uints.menu_xmb_theme, diff --git a/msg_hash.h b/msg_hash.h index f55e29f338..c5151f1314 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -761,6 +761,7 @@ enum msg_hash_enums MENU_LABEL(MENU_FONT_COLOR_GREEN), MENU_LABEL(MENU_FONT_COLOR_BLUE), MENU_LABEL(XMB_FONT), + MENU_LABEL(XMB_LAYOUT), MENU_LABEL(XMB_THEME), MENU_LABEL(XMB_MAIN_MENU_ENABLE_SETTINGS), MENU_LABEL(XMB_MENU_COLOR_THEME), From 1d84c0eca122334fbafa52e2dbd41b3a7a9327c0 Mon Sep 17 00:00:00 2001 From: gblues Date: Mon, 23 Apr 2018 23:22:27 -0700 Subject: [PATCH 445/517] Fix analog for DS3, plus some cleanups == DETAILS - DS3 analog wasn't working mainly because I forgot to actually declare the axes in input/input_autoconfig.c when declaring the pad. Whoops. - I also moved the axis decoding logic to a more central place, because it clearly is not Wii U specific. - Removed some dead commented-out code == TESTING Can use analog inputs on both GCA and DS3. Tested in Mario 3 on Nestopia core. Haven't tested with any actual analog games, but I did confirm via logging that the correct ranges are produced. --- Makefile.common | 1 + Makefile.wiiu | 9 ----- input/common/hid/device_ds3.c | 52 +++++++++++++--------------- input/common/hid/device_wiiu_gca.c | 7 ++-- input/common/hid/hid_device_driver.h | 1 + input/include/gamepad.h | 12 +++++-- input/input_autodetect_builtin.c | 11 +++++- input/input_types.h | 1 + wiiu/input/pad_functions.c | 15 +------- wiiu/input/wiiu_hid_types.h | 1 - wiiu/input/wiiu_input.h | 6 +--- 11 files changed, 52 insertions(+), 64 deletions(-) diff --git a/Makefile.common b/Makefile.common index 3743a0c805..98e00bd34d 100644 --- a/Makefile.common +++ b/Makefile.common @@ -198,6 +198,7 @@ OBJ += frontend/frontend.o \ $(LIBRETRO_COMM_DIR)/hash/rhash.o \ audio/audio_driver.o \ $(LIBRETRO_COMM_DIR)/audio/audio_mixer.o \ + input/common/input_common.o \ input/input_driver.o \ input/input_mapper.o \ led/led_driver.o \ diff --git a/Makefile.wiiu b/Makefile.wiiu index 519da84341..59b35b388b 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -50,15 +50,6 @@ ifeq ($(WIIU_HID),1) OBJ += wiiu/input/hidpad_driver.o OBJ += wiiu/input/wiiu_hid.o OBJ += input/connect/joypad_connection.o \ - input/connect/connect_ps2adapter.o \ - input/connect/connect_psxadapter.o \ - input/connect/connect_ps3.o \ - input/connect/connect_ps4.o \ - input/connect/connect_wii.o \ - input/connect/connect_nesusb.o \ - input/connect/connect_snesusb.o \ - input/connect/connect_wiiupro.o \ - input/connect/connect_wiiugca.o \ input/common/hid/hid_device_driver.o \ input/common/hid/device_wiiu_gca.o \ input/common/hid/device_ds3.o \ diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c index 1c87ec6595..4b26cd5634 100644 --- a/input/common/hid/device_ds3.c +++ b/input/common/hid/device_ds3.c @@ -25,6 +25,7 @@ typedef struct ds3_instance { int slot; bool led_set; uint32_t buttons; + int16_t analog_state[3][2]; uint16_t motors[2]; uint8_t data[64]; } ds3_instance_t; @@ -68,17 +69,8 @@ static int control_packet_size = sizeof(control_packet); extern pad_connection_interface_t ds3_pad_connection; -static void print_error(const char *fmt, int32_t errcode) -{ - int16_t err1, err2; - - err1 = errcode & 0x0000ffff; - err2 = ((errcode & 0xffff0000) >> 16); - - RARCH_ERR(fmt, err1, err2); -} - static void update_pad_state(ds3_instance_t *instance); +static void update_analog_state(ds3_instance_t *instance); static int32_t send_activation_packet(ds3_instance_t *instance) { @@ -93,8 +85,6 @@ static int32_t send_activation_packet(ds3_instance_t *instance) HID_SEND_CONTROL(instance->handle, activation_packet, sizeof(activation_packet)); #endif - if(result < 0) - print_error("[ds3]: activation packet failed (%d:%d)\n", result); return result; } @@ -104,8 +94,6 @@ static uint32_t set_protocol(ds3_instance_t *instance, int protocol) uint32_t result = 0; #if defined(WIIU) result = HID_SET_PROTOCOL(instance->handle, 1); - if(result) - print_error("[ds3]: set protocol failed (%d:%d)\n", result); #endif return result; @@ -134,8 +122,6 @@ static int32_t send_control_packet(ds3_instance_t *instance) DS3_RUMBLE_REPORT_ID, packet_buffer+PACKET_OFFSET, control_packet_size-PACKET_OFFSET); - if(result < 0) - print_error("[ds3]: send control packet failed: (%d:%d)\n", result); #else HID_SEND_CONTROL(instance->handle, packet_buffer+PACKET_OFFSET, @@ -157,10 +143,8 @@ static void *ds3_init(void *handle) instance->handle = handle; RARCH_LOG("[ds3]: setting protocol\n"); -/* - if(set_protocol(instance, 1)) - errors++; -*/ + + /* this might fail, but we don't care. */ set_protocol(instance, 1); RARCH_LOG("[ds3]: sending control packet\n"); @@ -275,6 +259,22 @@ static void ds3_packet_handler(void *data, uint8_t *packet, uint16_t size) memcpy(instance->data, packet, size); update_pad_state(instance); + update_analog_state(instance); +} + +static void update_analog_state(ds3_instance_t *instance) +{ + int pad_axis; + int16_t interpolated; + unsigned stick, axis; + + for(pad_axis = 0; pad_axis < 4; pad_axis++) + { + axis = pad_axis % 2 ? 0 : 1; + stick = pad_axis / 2; + interpolated = instance->data[6+pad_axis]; + instance->analog_state[stick][axis] = (interpolated - 128) * 256; + } } static void update_pad_state(ds3_instance_t *instance) @@ -318,17 +318,15 @@ static void ds3_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t static int16_t ds3_get_axis(void *data, unsigned axis) { + axis_data axis_data; ds3_instance_t *pad = (ds3_instance_t *)data; - int16_t val; - if(!pad || axis >= 4) + gamepad_read_axis_data(axis, &axis_data); + + if(!pad || axis_data.axis >= 4) return 0; - val = pad->data[6+axis]; - // val = pad->data[7+axis]; - val = (val - 128) * 256; - - return val; + return gamepad_get_axis_value(pad->analog_state, &axis_data); } static const char *ds3_get_name(void *data) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 7e82dda288..1a26800041 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -15,7 +15,6 @@ */ #include #include "hid_device_driver.h" -#include "../../../wiiu/input/wiiu_hid.h" #ifdef WII static uint8_t activation_packet[] = { 0x01, 0x13 }; @@ -259,9 +258,7 @@ static void update_analog_state(gca_pad_t *pad) { int pad_axis; int16_t interpolated; - int16_t stage1, stage2; unsigned stick, axis; - uint8_t val; /* GameCube analog axis are 8-bit unsigned, where 128/128 is center. * So, we subtract 128 to get a signed, 0-based value and then mulitply @@ -321,12 +318,12 @@ static int16_t wiiu_gca_get_axis(void *data, unsigned axis) gca_pad_t *pad = (gca_pad_t *)data; - pad_functions.read_axis_data(axis, &axis_data); + gamepad_read_axis_data(axis, &axis_data); if(!pad || axis_data.axis >= 4) return 0; - return pad_functions.get_axis_value(axis_data.axis, pad->analog_state, axis_data.is_negative); + return gamepad_get_axis_value(pad->analog_state, &axis_data); } static const char *wiiu_gca_get_name(void *data) diff --git a/input/common/hid/hid_device_driver.h b/input/common/hid/hid_device_driver.h index 57cd9f6e40..8e764f0f78 100644 --- a/input/common/hid/hid_device_driver.h +++ b/input/common/hid/hid_device_driver.h @@ -20,6 +20,7 @@ #include "../../input_driver.h" #include "../../connect/joypad_connection.h" #include "../../include/hid_driver.h" +#include "../../include/gamepad.h" #include "../../../verbosity.h" #include "../../../tasks/tasks_internal.h" diff --git a/input/include/gamepad.h b/input/include/gamepad.h index 8acb9027d1..d895b55f25 100644 --- a/input/include/gamepad.h +++ b/input/include/gamepad.h @@ -20,8 +20,16 @@ #include "../input_driver.h" -typedef struct pad_connection_listener_interface { +struct pad_connection_listener_interface { void (*connected)(unsigned port, input_device_driver_t *driver); -} pad_connection_listener_t; +}; + +typedef struct _axis_data { + int32_t axis; + bool is_negative; +} axis_data; + +void gamepad_read_axis_data(uint32_t axis, axis_data *data); +int16_t gamepad_get_axis_value(int16_t state[3][2], axis_data *data); #endif /* GAMEPAD_H__ */ diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index 72909ebb8e..db3443cd45 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -267,7 +267,16 @@ DECL_BTN_EX(down, 5, "D-Pad Down") \ DECL_BTN_EX(left, 6, "D-Pad left") \ DECL_BTN_EX(right, 7, "D-Pad Right") \ DECL_BTN_EX(r3, 15, "R3") \ -DECL_BTN_EX(l3, 14, "L3") +DECL_BTN_EX(l3, 14, "L3") \ +DECL_AXIS_EX(l_x_plus, +1, "L Analog right") \ +DECL_AXIS_EX(l_x_minus, -1, "L Analog left") \ +DECL_AXIS_EX(l_y_plus, +0, "L Analog up") \ +DECL_AXIS_EX(l_y_minus, -0, "L Analog down") \ +DECL_AXIS_EX(r_x_plus, +3, "R Analog right") \ +DECL_AXIS_EX(r_x_minus, -3, "R Analog left") \ +DECL_AXIS_EX(r_y_plus, +2, "R Analog up") \ +DECL_AXIS_EX(r_y_minus, -2, "R Analog down") + #define WIIUINPUT_GAMEPAD_DEFAULT_BINDS \ DECL_BTN_EX(menu_toggle, 1, "Home") \ diff --git a/input/input_types.h b/input/input_types.h index 0c1e2edcf1..0bc64d8c42 100644 --- a/input/input_types.h +++ b/input/input_types.h @@ -27,5 +27,6 @@ typedef struct { uint16_t analogs[8]; } input_bits_t; typedef struct joypad_connection joypad_connection_t; +typedef struct pad_connection_listener_interface pad_connection_listener_t; #endif /* __INPUT_TYPES__H */ diff --git a/wiiu/input/pad_functions.c b/wiiu/input/pad_functions.c index ad2c383090..5aefbaf23c 100644 --- a/wiiu/input/pad_functions.c +++ b/wiiu/input/pad_functions.c @@ -75,21 +75,8 @@ void wiiu_pad_set_axis_value(int16_t state[3][2], int16_t left_x, int16_t left_y state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_Y] = touch_y; } - -void wiiu_pad_read_axis_data(uint32_t axis, axis_data *data) -{ - data->axis = AXIS_POS_GET(axis); - data->is_negative = false; - - if(data->axis >= AXIS_INVALID) - { - data->axis = AXIS_NEG_GET(axis); - data->is_negative = true; - } -} - wiiu_pad_functions_t pad_functions = { wiiu_pad_get_axis_value, wiiu_pad_set_axis_value, - wiiu_pad_read_axis_data, + gamepad_read_axis_data, }; diff --git a/wiiu/input/wiiu_hid_types.h b/wiiu/input/wiiu_hid_types.h index 59eaabaddf..b73fcf3a6d 100644 --- a/wiiu/input/wiiu_hid_types.h +++ b/wiiu/input/wiiu_hid_types.h @@ -22,7 +22,6 @@ typedef struct wiiu_adapter wiiu_adapter_t; typedef struct wiiu_attach wiiu_attach_event; typedef struct _wiiu_event_list wiiu_event_list; typedef struct _wiiu_adapter_list wiiu_adapter_list; -typedef struct _axis_data axis_data; typedef struct _wiiu_pad_functions wiiu_pad_functions_t; #endif /* __WIIU_HID_TYPES__H */ diff --git a/wiiu/input/wiiu_input.h b/wiiu/input/wiiu_input.h index 16509b47f2..37416bc7e6 100644 --- a/wiiu/input/wiiu_input.h +++ b/wiiu/input/wiiu_input.h @@ -18,6 +18,7 @@ #define __WIIU_INPUT__H #include "wiiu_hid_types.h" +#include "../../input/include/gamepad.h" #ifdef HAVE_CONFIG_H #include "../../config.h" @@ -56,11 +57,6 @@ #define WIIU_ANALOG_FACTOR 0x7ff0 #define WIIU_READ_STICK(stick) ((stick) * WIIU_ANALOG_FACTOR) -struct _axis_data { - int32_t axis; - bool is_negative; -}; - struct _wiiu_pad_functions { int16_t (*get_axis_value)(int32_t axis, int16_t state[3][2], bool is_negative); void (*set_axis_value)(int16_t state[3][2], int16_t left_x, int16_t left_y, From 64bdf7e48e6c8b82d34b83fb474043334e576060 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 08:33:14 +0200 Subject: [PATCH 446/517] Prevent collision --- config.def.h | 2 +- configuration.c | 2 +- menu/menu_setting.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config.def.h b/config.def.h index 3d4a78e496..55653b7dc8 100644 --- a/config.def.h +++ b/config.def.h @@ -305,7 +305,7 @@ static unsigned xmb_alpha_factor = 75; static unsigned menu_font_color_red = 255; static unsigned menu_font_color_green = 255; static unsigned menu_font_color_blue = 255; -static unsigned xmb_layout = 0; +static unsigned xmb_menu_layout = 0; static unsigned xmb_icon_theme = XMB_ICON_THEME_MONOCHROME; static unsigned xmb_theme = XMB_THEME_ELECTRIC_BLUE; #if defined(HAVE_LAKKA) || defined(__arm__) || defined(__PPC64__) || defined(__ppc64__) || defined(__powerpc64__) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__) diff --git a/configuration.c b/configuration.c index 16b1c44ba9..bfd2615f37 100644 --- a/configuration.c +++ b/configuration.c @@ -1474,7 +1474,7 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("menu_left_thumbnails", &settings->uints.menu_left_thumbnails, true, menu_left_thumbnails_default, false); SETTING_UINT("xmb_alpha_factor", &settings->uints.menu_xmb_alpha_factor, true, xmb_alpha_factor, false); SETTING_UINT("xmb_scale_factor", &settings->uints.menu_xmb_scale_factor, true, xmb_scale_factor, false); - SETTING_UINT("xmb_layout", &settings->uints.menu_xmb_layout, true, xmb_layout, false); + SETTING_UINT("xmb_layout", &settings->uints.menu_xmb_layout, true, xmb_menu_layout, false); SETTING_UINT("xmb_theme", &settings->uints.menu_xmb_theme, true, xmb_icon_theme, false); SETTING_UINT("xmb_menu_color_theme", &settings->uints.menu_xmb_color_theme, true, xmb_theme, false); SETTING_UINT("menu_font_color_red", &settings->uints.menu_font_color_red, true, menu_font_color_red, false); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 79651c3743..63ee542fc0 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5462,7 +5462,7 @@ static bool setting_append_list( &settings->uints.menu_xmb_layout, MENU_ENUM_LABEL_XMB_LAYOUT, MENU_ENUM_LABEL_VALUE_XMB_LAYOUT, - xmb_layout, + xmb_menu_layout, &group_info, &subgroup_info, parent_group, From 381bc285303c245bc4007e6d5de60768c1740495 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 13:19:37 +0200 Subject: [PATCH 447/517] Update CHANGES.md --- CHANGES.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c1e3c076e9..18c30f1c01 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ -# 1.7.2 (future) +# 1.7.3 (future) + +# 1.7.2 - ANDROID/OPENSL: Prevent crashes when setting audio latency too low (buffer count can never be lower than 2 now). +- CRT: Added CRT SwitchRes. - COMMON: Hide the 'Core delete' option if the 'Core updater' is also hidden. - COMMON: Add way to reset core association for playlist entry. - COMMON: Fix invalid long command line options causing infinite loop on Windows @@ -10,18 +13,26 @@ - CHEEVOS: Fixed incompatibilities with Neo Geo Pocket achievement sets. - D3D10: Added D3D10 driver to release build. Has working shaders (Slang), overlay, and menu display driver support. Should be on par capabilities wise with D3D11 driver except for there being no hardware rendering right now. -- D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. +- D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. First core to use this is PPSSPP. +- D3D11: Increase backwards compatibility, shaders compile with Shader Model 4.0 now, added support for more feature levels. - D3D11/D3D12: Fix crashes with completely black or white thumbnail textures in XMB. - LIBRETRO: Addition - Functions to enable and disable audio and video, and an environment function to query status of audio and video enables. - LOCALIZATION: Update Italian translation. - LOCALIZATION: Update Polish translation. -- MENU: Disable XMB shadow icons by default for PowerPC and ARM for performance reasons. +- MENU: Add Rewind/Latency/Overlay settings to Quick Menu +- MENU/XMB: Disable XMB shadow icons by default for PowerPC and ARM for performance reasons. - MENU/XMB: Left/right thumbnails are now automatically scaled according to layout. - MENU/XMB: Add Left Thumbnails (additional to the right). - MENU/XMB: Fixed left/right tab regression. - MENU/XMB: Fix scaling of tall images that were cut on bottom previously. - MENU/XMB: Menu scale factor setting now changes texts length, image scaling and margins. - MENU/XMB: Mouse cursor scales correctly now. +- MENU/XMB: Add toggle to show/hide Playlist tabs. +- MENU/XMB: Add menu layout - can switch between Desktop, Handheld and Auto. +- MENU/XMB: Don't load menu pipeline shaders unless XMB is selected (D3D10/D3D11/D3D12/GL/Vulkan) +- MENU/VIDEO: Only show black frame insertion for the video drivers/context drivers +that support it (so far this includes - D3D8/D3D9, OpenGL, Vulkan) +- MENU/VIDEO: Only show max swapchain images if supported by video driver and/or context driver (so far this includes - DRM EGL context driver, VideoCore EGL context driver, Vulkan) - MENU/MaterialUI: Automatic DPI Scaling should be much improved now, now scales as expected at 1440p and 4K resolutions. - MENU/MaterialUI: Fix wrong calculation of an entry height causing long playlists to end up outside of screen range. This also could cause crashes on low DPI screens. - IOS: Fixed crash when opening downloaded roms from Safari or using the "Open in.." functionality. Added the compiler flag to support keyboard remapping to controls. From 5164e621724f1d2cd52ad1192c76bc8663c47597 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 13:46:14 +0200 Subject: [PATCH 448/517] User Interface -> Views - show 'no entries to display' instead of getting locked in --- CHANGES.md | 1 + menu/menu_displaylist.c | 219 +++++++++++++++++++++++----------------- menu/menu_setting.c | 43 ++++---- 3 files changed, 153 insertions(+), 110 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 18c30f1c01..1a077853d0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -38,6 +38,7 @@ that support it (so far this includes - D3D8/D3D9, OpenGL, Vulkan) - IOS: Fixed crash when opening downloaded roms from Safari or using the "Open in.." functionality. Added the compiler flag to support keyboard remapping to controls. - IOS: Fixed buffer overlap that caused a crash while trying to download GLSL shaders from the buildbot. - PS3: fix URLS +- SCANNER: Should be able to scan dual-layer Wii disc images now, filestream code now supports files larger than 4GB. - SHADERS/SLANG: Slang shaders should work again on Android version and MSVC versions (basically all the Griffin-based versions). - SHADERS: If GL context is GLES2/3/Core context, Cg shaders are unavailable. Applies to shader list too. - SHADERS: Hide cg/glsl shaders from being able to be selected if D3D8/9/10/11/Vulkan video drivers are selected. diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 0a8e01f39c..fadc28e90b 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5303,98 +5303,137 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) info->need_push = true; break; case DISPLAYLIST_MENU_SETTINGS_LIST: - menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); + { + unsigned count = 0; + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_WALLPAPER, - PARSE_ONLY_PATH, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_DYNAMIC_WALLPAPER, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_WALLPAPER_OPACITY, - PARSE_ONLY_FLOAT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_FRAMEBUFFER_OPACITY, - PARSE_ONLY_FLOAT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_LINEAR_FILTER, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_HORIZONTAL_ANIMATION, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_ENTRY_NORMAL_COLOR, - PARSE_ONLY_HEX, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_ENTRY_HOVER_COLOR, - PARSE_ONLY_HEX, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_TITLE_COLOR, - PARSE_ONLY_HEX, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_DPI_OVERRIDE_ENABLE, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_DPI_OVERRIDE_VALUE, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_ALPHA_FACTOR, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_SCALE_FACTOR, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_FONT, - PARSE_ONLY_PATH, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_FONT_COLOR_RED, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_FONT_COLOR_GREEN, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_FONT_COLOR_BLUE, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_LAYOUT, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_THEME, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_SHADOWS_ENABLE, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_RIBBON_ENABLE, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_MENU_COLOR_THEME, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MATERIALUI_ICONS_ENABLE, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MATERIALUI_MENU_COLOR_THEME, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MATERIALUI_MENU_HEADER_OPACITY, - PARSE_ONLY_FLOAT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MATERIALUI_MENU_FOOTER_OPACITY, - PARSE_ONLY_FLOAT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_THUMBNAILS, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_LEFT_THUMBNAILS, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_VERTICAL_THUMBNAILS, - PARSE_ONLY_BOOL, false); + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_WALLPAPER, + PARSE_ONLY_PATH, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_DYNAMIC_WALLPAPER, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_WALLPAPER_OPACITY, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_FRAMEBUFFER_OPACITY, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_LINEAR_FILTER, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_HORIZONTAL_ANIMATION, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_ENTRY_NORMAL_COLOR, + PARSE_ONLY_HEX, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_ENTRY_HOVER_COLOR, + PARSE_ONLY_HEX, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_TITLE_COLOR, + PARSE_ONLY_HEX, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_DPI_OVERRIDE_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_DPI_OVERRIDE_VALUE, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_ALPHA_FACTOR, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_SCALE_FACTOR, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_FONT, + PARSE_ONLY_PATH, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_FONT_COLOR_RED, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_FONT_COLOR_GREEN, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_FONT_COLOR_BLUE, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_LAYOUT, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_THEME, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_SHADOWS_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_RIBBON_ENABLE, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_MENU_COLOR_THEME, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MATERIALUI_ICONS_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MATERIALUI_MENU_COLOR_THEME, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MATERIALUI_MENU_HEADER_OPACITY, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MATERIALUI_MENU_FOOTER_OPACITY, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_THUMBNAILS, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_LEFT_THUMBNAILS, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_VERTICAL_THUMBNAILS, + PARSE_ONLY_BOOL, false) == 0) + count++; - info->need_refresh = true; - info->need_push = true; + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY), + msg_hash_to_str(MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY), + MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY, + FILE_TYPE_NONE, 0, 0); + + info->need_refresh = true; + info->need_push = true; + } break; case DISPLAYLIST_USER_INTERFACE_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 63ee542fc0..c2a792f1b9 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5174,28 +5174,31 @@ static bool setting_append_list( SD_FLAG_ADVANCED ); - CONFIG_BOOL( - list, list_info, - &settings->bools.menu_horizontal_animation, - MENU_ENUM_LABEL_MENU_HORIZONTAL_ANIMATION, - MENU_ENUM_LABEL_VALUE_MENU_HORIZONTAL_ANIMATION, - true, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler, - SD_FLAG_ADVANCED - ); - + if (string_is_equal(settings->arrays.menu_driver, "xmb")) + { + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_horizontal_animation, + MENU_ENUM_LABEL_MENU_HORIZONTAL_ANIMATION, + MENU_ENUM_LABEL_VALUE_MENU_HORIZONTAL_ANIMATION, + true, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_ADVANCED + ); #ifdef RARCH_MOBILE - /* We don't want mobile users being able to switch this off. */ - (*list)[list_info->index - 1].action_left = NULL; - (*list)[list_info->index - 1].action_right = NULL; - (*list)[list_info->index - 1].action_start = NULL; + /* We don't want mobile users being able to switch this off. */ + (*list)[list_info->index - 1].action_left = NULL; + (*list)[list_info->index - 1].action_right = NULL; + (*list)[list_info->index - 1].action_start = NULL; #endif + } + END_SUB_GROUP(list, list_info, parent_group); From 757d1baa8d3cc864966e3631162b0727e9037b34 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 13:51:27 +0200 Subject: [PATCH 449/517] Cleanups --- menu/menu_displaylist.c | 6 ------ menu/menu_setting.c | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index fadc28e90b..ae878ee242 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5032,10 +5032,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_ONSCREEN_DISPLAY_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); -#ifdef HAVE_OVERLAY menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS, PARSE_ACTION, false); -#endif menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_ONSCREEN_NOTIFICATIONS_SETTINGS, PARSE_ACTION, false); @@ -5163,16 +5161,12 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_MENU_SHOW_LOAD_CONTENT, PARSE_ONLY_BOOL, false); -#if defined(HAVE_NETWORKING) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_SHOW_ONLINE_UPDATER, PARSE_ONLY_BOOL, false); -#if !defined(HAVE_LAKKA) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_SHOW_CORE_UPDATER, PARSE_ONLY_BOOL, false); -#endif -#endif menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_SHOW_INFORMATION, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index c2a792f1b9..290924c23d 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -2331,6 +2331,7 @@ static bool setting_append_list( parent_group); settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); +#ifdef HAVE_OVERLAY CONFIG_ACTION( list, list_info, MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS, @@ -2339,6 +2340,7 @@ static bool setting_append_list( &subgroup_info, parent_group); settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); +#endif CONFIG_ACTION( list, list_info, @@ -6122,6 +6124,7 @@ static bool setting_append_list( settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); #endif +#ifdef HAVE_NETWORKING CONFIG_BOOL( list, list_info, &settings->bools.menu_show_online_updater, @@ -6137,6 +6140,7 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); +#if !defined(HAVE_LAKKA) CONFIG_BOOL( list, list_info, &settings->bools.menu_show_core_updater, @@ -6151,6 +6155,8 @@ static bool setting_append_list( general_write_handler, general_read_handler, SD_FLAG_NONE); +#endif +#endif CONFIG_BOOL( list, list_info, From f228b2675584d167614a974f796b2bc6ba698356 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 14:08:38 +0200 Subject: [PATCH 450/517] Cleanups --- menu/menu_displaylist.c | 750 +++++++++++++++++++++------------------- menu/menu_setting.c | 5 +- 2 files changed, 389 insertions(+), 366 deletions(-) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index ae878ee242..317df85fa0 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -4191,6 +4191,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) { size_t i; menu_ctx_displaylist_t disp_list; + unsigned count = 0; int ret = 0; core_info_list_t *list = NULL; menu_handle_t *menu = NULL; @@ -4643,7 +4644,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) const char *core_name = cores_names->elems[i].data; if ( !path_is_empty(RARCH_PATH_CORE) && - string_is_equal(core_path, path_get(RARCH_PATH_CORE))) + string_is_equal(core_path, path_get(RARCH_PATH_CORE))) { strlcpy(new_path_entry, core_path, sizeof(new_path_entry)); snprintf(new_entry, sizeof(new_entry), "Current core (%s)", core_name); @@ -4736,7 +4737,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) const char *core_name = cores_names->elems[i].data; if ( !path_is_empty(RARCH_PATH_CORE) && - string_is_equal(core_path, path_get(RARCH_PATH_CORE))) + string_is_equal(core_path, path_get(RARCH_PATH_CORE))) { strlcpy(new_path_entry, core_path, sizeof(new_path_entry)); snprintf(new_entry, sizeof(new_entry), "Current core (%s)", core_name); @@ -4879,8 +4880,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) for (i = 0; i < RARCH_BIND_LIST_END; i++) { ret = menu_displaylist_parse_settings_enum(menu, info, - (enum msg_hash_enums)(MENU_ENUM_LABEL_INPUT_HOTKEY_BIND_BEGIN + i), - PARSE_ONLY_BIND, false); + (enum msg_hash_enums)(MENU_ENUM_LABEL_INPUT_HOTKEY_BIND_BEGIN + i), + PARSE_ONLY_BIND, false); (void)ret; } } @@ -5180,7 +5181,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_MENU_SHOW_HELP, PARSE_ONLY_BOOL, false); -#if defined(HAVE_LAKKA) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_SHOW_QUIT_RETROARCH, PARSE_ONLY_BOOL, false); @@ -5188,7 +5188,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_SHOW_REBOOT, PARSE_ONLY_BOOL, false); -#endif menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_SETTINGS, @@ -5199,22 +5198,18 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) PARSE_ONLY_STRING, false); menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_CONTENT_SHOW_FAVORITES, - PARSE_ONLY_BOOL, false); + MENU_ENUM_LABEL_CONTENT_SHOW_FAVORITES, + PARSE_ONLY_BOOL, false); -#ifdef HAVE_IMAGEVIEWER menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_IMAGES, PARSE_ONLY_BOOL, false); -#endif menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_MUSIC, PARSE_ONLY_BOOL, false); -#ifdef HAVE_FFMPEG menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_VIDEO, PARSE_ONLY_BOOL, false); -#endif #ifdef HAVE_NETWORKING menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_NETPLAY, @@ -5223,11 +5218,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_HISTORY, PARSE_ONLY_BOOL, false); -#ifdef HAVE_LIBRETRODB menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_ADD, PARSE_ONLY_BOOL, false); -#endif menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_PLAYLISTS, PARSE_ONLY_BOOL, false); @@ -5297,137 +5290,134 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) info->need_push = true; break; case DISPLAYLIST_MENU_SETTINGS_LIST: - { - unsigned count = 0; - menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_WALLPAPER, - PARSE_ONLY_PATH, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_DYNAMIC_WALLPAPER, - PARSE_ONLY_BOOL, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_WALLPAPER_OPACITY, - PARSE_ONLY_FLOAT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_FRAMEBUFFER_OPACITY, - PARSE_ONLY_FLOAT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_LINEAR_FILTER, - PARSE_ONLY_BOOL, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_HORIZONTAL_ANIMATION, - PARSE_ONLY_BOOL, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_ENTRY_NORMAL_COLOR, - PARSE_ONLY_HEX, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_ENTRY_HOVER_COLOR, - PARSE_ONLY_HEX, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_TITLE_COLOR, - PARSE_ONLY_HEX, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_DPI_OVERRIDE_ENABLE, - PARSE_ONLY_BOOL, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_DPI_OVERRIDE_VALUE, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_ALPHA_FACTOR, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_SCALE_FACTOR, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_FONT, - PARSE_ONLY_PATH, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_FONT_COLOR_RED, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_FONT_COLOR_GREEN, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MENU_FONT_COLOR_BLUE, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_LAYOUT, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_THEME, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_SHADOWS_ENABLE, - PARSE_ONLY_BOOL, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_RIBBON_ENABLE, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_MENU_COLOR_THEME, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MATERIALUI_ICONS_ENABLE, - PARSE_ONLY_BOOL, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MATERIALUI_MENU_COLOR_THEME, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MATERIALUI_MENU_HEADER_OPACITY, - PARSE_ONLY_FLOAT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_MATERIALUI_MENU_FOOTER_OPACITY, - PARSE_ONLY_FLOAT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_THUMBNAILS, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_LEFT_THUMBNAILS, - PARSE_ONLY_UINT, false) == 0) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_XMB_VERTICAL_THUMBNAILS, - PARSE_ONLY_BOOL, false) == 0) - count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_WALLPAPER, + PARSE_ONLY_PATH, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_DYNAMIC_WALLPAPER, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_WALLPAPER_OPACITY, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_FRAMEBUFFER_OPACITY, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_LINEAR_FILTER, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_HORIZONTAL_ANIMATION, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_ENTRY_NORMAL_COLOR, + PARSE_ONLY_HEX, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_ENTRY_HOVER_COLOR, + PARSE_ONLY_HEX, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_TITLE_COLOR, + PARSE_ONLY_HEX, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_DPI_OVERRIDE_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_DPI_OVERRIDE_VALUE, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_ALPHA_FACTOR, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_SCALE_FACTOR, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_FONT, + PARSE_ONLY_PATH, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_FONT_COLOR_RED, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_FONT_COLOR_GREEN, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_FONT_COLOR_BLUE, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_LAYOUT, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_THEME, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_SHADOWS_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_RIBBON_ENABLE, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_MENU_COLOR_THEME, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MATERIALUI_ICONS_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MATERIALUI_MENU_COLOR_THEME, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MATERIALUI_MENU_HEADER_OPACITY, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MATERIALUI_MENU_FOOTER_OPACITY, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_THUMBNAILS, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_LEFT_THUMBNAILS, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_XMB_VERTICAL_THUMBNAILS, + PARSE_ONLY_BOOL, false) == 0) + count++; - if (count == 0) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY), - msg_hash_to_str(MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY), - MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY, - FILE_TYPE_NONE, 0, 0); + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY), + msg_hash_to_str(MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY), + MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY, + FILE_TYPE_NONE, 0, 0); - info->need_refresh = true; - info->need_push = true; - } + info->need_refresh = true; + info->need_push = true; break; case DISPLAYLIST_USER_INTERFACE_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); @@ -5499,8 +5489,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) if (string_is_equal(settings->arrays.menu_driver, "xmb")) { menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_CHEEVOS_BADGES_ENABLE, - PARSE_ONLY_BOOL, false); + MENU_ENUM_LABEL_CHEEVOS_BADGES_ENABLE, + PARSE_ONLY_BOOL, false); } menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CHEEVOS_TEST_UNOFFICIAL, @@ -5517,28 +5507,25 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_UPDATER_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - { - unsigned count = 0; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_CORE_UPDATER_BUILDBOT_URL, - PARSE_ONLY_STRING, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CORE_UPDATER_BUILDBOT_URL, + PARSE_ONLY_STRING, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_BUILDBOT_ASSETS_URL, PARSE_ONLY_STRING, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE, PARSE_ONLY_BOOL, false) != -1) - count++; + count++; - if (count == 0) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), - msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), - MENU_ENUM_LABEL_NO_SETTINGS_FOUND, - 0, 0, 0); - } + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); info->need_refresh = true; info->need_push = true; @@ -5594,146 +5581,158 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_NETWORK_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - { - unsigned user; - unsigned count = 0; - if (menu_displaylist_parse_settings_enum(menu, info, + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE, PARSE_ONLY_BOOL, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_USE_MITM_SERVER, PARSE_ONLY_BOOL, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_MITM_SERVER, PARSE_ONLY_STRING, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_IP_ADDRESS, PARSE_ONLY_STRING, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT, PARSE_ONLY_UINT, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_PASSWORD, PARSE_ONLY_STRING, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_SPECTATE_PASSWORD, PARSE_ONLY_STRING, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR, PARSE_ONLY_BOOL, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_ALLOW_SLAVES, PARSE_ONLY_BOOL, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_REQUIRE_SLAVES, PARSE_ONLY_BOOL, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE, PARSE_ONLY_BOOL, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES, PARSE_ONLY_INT, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN, PARSE_ONLY_INT, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, PARSE_ONLY_INT, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL, PARSE_ONLY_BOOL, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_SHARE_DIGITAL, PARSE_ONLY_UINT, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_SHARE_ANALOG, PARSE_ONLY_UINT, false) != -1) - count++; + count++; + + { + unsigned user; for (user = 0; user < MAX_USERS; user++) { if (menu_displaylist_parse_settings_enum(menu, info, - (enum msg_hash_enums)(MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_1 + user), - PARSE_ONLY_BOOL, false) != -1) + (enum msg_hash_enums)(MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_1 + user), + PARSE_ONLY_BOOL, false) != -1) count++; } - if (menu_displaylist_parse_settings_enum(menu, info, + } + + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETWORK_CMD_ENABLE, PARSE_ONLY_BOOL, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETWORK_CMD_PORT, PARSE_ONLY_UINT, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETWORK_REMOTE_ENABLE, PARSE_ONLY_BOOL, false) != -1) - count++; - if (menu_displaylist_parse_settings_enum(menu, info, + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETWORK_REMOTE_PORT, PARSE_ONLY_UINT, false) != -1) - count++; + count++; + { + unsigned user; + unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); + for(user = 0; user < max_users; user++) { - unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); - for(user = 0; user < max_users; user++) - { - if (menu_displaylist_parse_settings_enum(menu, info, - (enum msg_hash_enums)(MENU_ENUM_LABEL_NETWORK_REMOTE_USER_1_ENABLE + user), - PARSE_ONLY_BOOL, false) != -1) - count++; - } + if (menu_displaylist_parse_settings_enum(menu, info, + (enum msg_hash_enums)(MENU_ENUM_LABEL_NETWORK_REMOTE_USER_1_ENABLE + user), + PARSE_ONLY_BOOL, false) != -1) + count++; } + } - if (menu_displaylist_parse_settings_enum(menu, info, + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_STDIN_CMD_ENABLE, PARSE_ONLY_BOOL, false) != -1) - count++; + count++; - if (menu_displaylist_parse_settings_enum(menu, info, + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_UPDATER_SETTINGS, PARSE_ACTION, false) != -1) - count++; + count++; - if (count == 0) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), - msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), - MENU_ENUM_LABEL_NO_SETTINGS_FOUND, - 0, 0, 0); - } + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); info->need_refresh = true; info->need_push = true; break; case DISPLAYLIST_LAKKA_SERVICES_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - menu_displaylist_parse_settings_enum(menu, info, + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_SSH_ENABLE, - PARSE_ONLY_BOOL, false); + PARSE_ONLY_BOOL, false) == 0) + count++; - menu_displaylist_parse_settings_enum(menu, info, + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_SAMBA_ENABLE, - PARSE_ONLY_BOOL, false); + PARSE_ONLY_BOOL, false) == 0) + count++; - menu_displaylist_parse_settings_enum(menu, info, + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_BLUETOOTH_ENABLE, - PARSE_ONLY_BOOL, false); + PARSE_ONLY_BOOL, false) == 0) + count++; + + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); info->need_refresh = true; info->need_push = true; @@ -5836,27 +5835,25 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_PRIVACY_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - { - bool available = false; - if (menu_displaylist_parse_settings_enum(menu, info, + + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CAMERA_ALLOW, PARSE_ONLY_BOOL, false) == 0) - available = true; - if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_LOCATION_ALLOW, - PARSE_ONLY_BOOL, true) == 0) - available = true; + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_LOCATION_ALLOW, + PARSE_ONLY_BOOL, true) == 0) + count++; - if (!available) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), - msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), - MENU_ENUM_LABEL_NO_SETTINGS_FOUND, - 0, 0, 0); + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); - info->need_refresh = true; - info->need_push = true; - } + info->need_refresh = true; + info->need_push = true; break; case DISPLAYLIST_VIDEO_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); @@ -5866,12 +5863,12 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_SCREEN_RESOLUTION, PARSE_ACTION, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_CRT_SWITCH_RESOLUTION, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_CRT_SWITCH_RESOLUTION_SUPER, - PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CRT_SWITCH_RESOLUTION, + PARSE_ONLY_BOOL, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CRT_SWITCH_RESOLUTION_SUPER, + PARSE_ONLY_UINT, false); menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_PAL60_ENABLE, PARSE_ONLY_BOOL, false); @@ -6046,7 +6043,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_AUDIO_DSP_PLUGIN, PARSE_ONLY_PATH, false); -#ifdef HAVE_WASAPI menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_AUDIO_WASAPI_EXCLUSIVE_MODE, PARSE_ONLY_BOOL, false); @@ -6056,7 +6052,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH, PARSE_ONLY_INT, false); -#endif info->need_refresh = true; info->need_push = true; @@ -6066,11 +6061,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_MAX_USERS, PARSE_ONLY_UINT, false); -#if TARGET_OS_IPHONE ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_SMALL_KEYBOARD_ENABLE, PARSE_ONLY_BOOL, false); -#endif ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_UNIFIED_MENU_CONTROLS, PARSE_ONLY_BOOL, false); @@ -6080,14 +6073,12 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_KEYBOARD_GAMEPAD_MAPPING_TYPE, PARSE_ONLY_UINT, false); -#ifdef VITA ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_TOUCH_ENABLE, PARSE_ONLY_BOOL, false); ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_PREFER_FRONT_TOUCH, PARSE_ONLY_BOOL, false); -#endif ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_MENU_ENUM_TOGGLE_GAMEPAD_COMBO, PARSE_ONLY_UINT, false); ret = menu_displaylist_parse_settings_enum(menu, info, @@ -6129,37 +6120,52 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) info->need_refresh = true; info->need_push = true; break; - case DISPLAYLIST_LATENCY_SETTINGS_LIST: + case DISPLAYLIST_LATENCY_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - menu_displaylist_parse_settings_enum(menu, info, + + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_HARD_SYNC, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_HARD_SYNC_FRAMES, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_FRAME_DELAY, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_AUDIO_LATENCY, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_POLL_TYPE_BEHAVIOR, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_RUN_AHEAD_ENABLED, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, - PARSE_ONLY_UINT, false); -#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, - PARSE_ONLY_BOOL, false); -#endif + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_RUN_AHEAD_ENABLED, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, + PARSE_ONLY_BOOL, false) == 0) + count++; + + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); info->need_refresh = true; info->need_push = true; @@ -6284,33 +6290,34 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_FAVORITES, MENU_SETTING_ACTION, 0, 0); - if (settings->bools.menu_content_show_favorites) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES), - msg_hash_to_str(MENU_ENUM_LABEL_GOTO_FAVORITES), - MENU_ENUM_LABEL_GOTO_FAVORITES, - MENU_SETTING_ACTION, 0, 0); + if (settings->bools.menu_content_show_favorites) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES), + msg_hash_to_str(MENU_ENUM_LABEL_GOTO_FAVORITES), + MENU_ENUM_LABEL_GOTO_FAVORITES, + MENU_SETTING_ACTION, 0, 0); - if (settings->bools.menu_content_show_images) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_IMAGES), - msg_hash_to_str(MENU_ENUM_LABEL_GOTO_IMAGES), - MENU_ENUM_LABEL_GOTO_IMAGES, - MENU_SETTING_ACTION, 0, 0); - if (settings->bools.menu_content_show_music) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_MUSIC), - msg_hash_to_str(MENU_ENUM_LABEL_GOTO_MUSIC), - MENU_ENUM_LABEL_GOTO_MUSIC, - MENU_SETTING_ACTION, 0, 0); + if (settings->bools.menu_content_show_images) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_IMAGES), + msg_hash_to_str(MENU_ENUM_LABEL_GOTO_IMAGES), + MENU_ENUM_LABEL_GOTO_IMAGES, + MENU_SETTING_ACTION, 0, 0); + + if (settings->bools.menu_content_show_music) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_MUSIC), + msg_hash_to_str(MENU_ENUM_LABEL_GOTO_MUSIC), + MENU_ENUM_LABEL_GOTO_MUSIC, + MENU_SETTING_ACTION, 0, 0); #ifdef HAVE_FFMPEG - if (settings->bools.menu_content_show_video) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_VIDEO), - msg_hash_to_str(MENU_ENUM_LABEL_GOTO_VIDEO), - MENU_ENUM_LABEL_GOTO_VIDEO, - MENU_SETTING_ACTION, 0, 0); + if (settings->bools.menu_content_show_video) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_VIDEO), + msg_hash_to_str(MENU_ENUM_LABEL_GOTO_VIDEO), + MENU_ENUM_LABEL_GOTO_VIDEO, + MENU_SETTING_ACTION, 0, 0); #endif if (core_info_list_num_info_files(list)) @@ -6419,24 +6426,37 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_RECORDING_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - menu_displaylist_parse_settings_enum(menu, info, + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_RECORD_ENABLE, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_RECORD_CONFIG, - PARSE_ONLY_PATH, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_PATH, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_RECORD_PATH, - PARSE_ONLY_STRING, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_STRING, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_RECORD_USE_OUTPUT_DIRECTORY, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_GPU_RECORD, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_POST_FILTER_RECORD, - PARSE_ONLY_BOOL, false); + PARSE_ONLY_BOOL, false) == 0) + count++; + + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); info->need_push = true; break; @@ -6463,61 +6483,61 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) if (frontend_driver_has_fork()) #endif { - if (settings->bools.menu_show_load_core) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_CORE_LIST, PARSE_ACTION, false); + if (settings->bools.menu_show_load_core) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CORE_LIST, PARSE_ACTION, false); } - if (settings->bools.menu_show_load_content) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_LOAD_CONTENT_LIST, - PARSE_ACTION, false); - if (settings->bools.menu_content_show_history) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY, - PARSE_ACTION, false); - if (settings->bools.menu_content_show_add) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_ADD_CONTENT_LIST, - PARSE_ACTION, false); + if (settings->bools.menu_show_load_content) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_LOAD_CONTENT_LIST, + PARSE_ACTION, false); + if (settings->bools.menu_content_show_history) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY, + PARSE_ACTION, false); + if (settings->bools.menu_content_show_add) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_ADD_CONTENT_LIST, + PARSE_ACTION, false); #ifdef HAVE_NETWORKING - if (settings->bools.menu_content_show_netplay) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETPLAY, - PARSE_ACTION, false); - if (settings->bools.menu_show_online_updater) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_ONLINE_UPDATER, - PARSE_ACTION, false); + if (settings->bools.menu_content_show_netplay) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY, + PARSE_ACTION, false); + if (settings->bools.menu_show_online_updater) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_ONLINE_UPDATER, + PARSE_ACTION, false); #endif menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_SETTINGS, PARSE_ACTION, false); - if (settings->bools.menu_show_information) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_INFORMATION_LIST, - PARSE_ACTION, false); + if (settings->bools.menu_show_information) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_INFORMATION_LIST, + PARSE_ACTION, false); #ifndef HAVE_DYNAMIC menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_RESTART_RETROARCH, PARSE_ACTION, false); #endif - if (settings->bools.menu_show_configurations) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_CONFIGURATIONS_LIST, - PARSE_ACTION, false); - if (settings->bools.menu_show_help) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_HELP_LIST, - PARSE_ACTION, false); - if (settings->bools.menu_show_quit_retroarch) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_QUIT_RETROARCH, - PARSE_ACTION, false); + if (settings->bools.menu_show_configurations) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CONFIGURATIONS_LIST, + PARSE_ACTION, false); + if (settings->bools.menu_show_help) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_HELP_LIST, + PARSE_ACTION, false); + if (settings->bools.menu_show_quit_retroarch) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_QUIT_RETROARCH, + PARSE_ACTION, false); #if defined(HAVE_LAKKA) - if (settings->bools.menu_show_reboot) - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_REBOOT, - PARSE_ACTION, false); + if (settings->bools.menu_show_reboot) + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_REBOOT, + PARSE_ACTION, false); menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_SHUTDOWN, PARSE_ACTION, false); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 290924c23d..803540ee3c 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -4656,6 +4656,7 @@ static bool setting_append_list( general_read_handler); menu_settings_list_current_add_range(list, list_info, 1, 6, 1, true, true); +#ifdef HAVE_DYNAMIC CONFIG_BOOL( list, list_info, &settings->bools.run_ahead_secondary_instance, @@ -4671,6 +4672,7 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE ); +#endif CONFIG_BOOL( list, list_info, @@ -5610,6 +5612,8 @@ static bool setting_append_list( general_read_handler, SD_FLAG_LAKKA_ADVANCED); + +#ifdef HAVE_LAKKA CONFIG_BOOL( list, list_info, &settings->bools.menu_show_quit_retroarch, @@ -5625,7 +5629,6 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); -#ifdef HAVE_LAKKA CONFIG_BOOL( list, list_info, &settings->bools.menu_show_reboot, From 77d97d2407cfe926c6dab40b872945be4cb4da9e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 14:24:25 +0200 Subject: [PATCH 451/517] (menu_displaylist.c) Cleanups --- menu/menu_displaylist.c | 120 +++++++++++++++++++++------------------- menu/menu_setting.c | 6 +- 2 files changed, 68 insertions(+), 58 deletions(-) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 317df85fa0..1ea2c825a6 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5210,11 +5210,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_VIDEO, PARSE_ONLY_BOOL, false); -#ifdef HAVE_NETWORKING menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_NETPLAY, PARSE_ONLY_BOOL, false); -#endif menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONTENT_SHOW_HISTORY, PARSE_ONLY_BOOL, false); @@ -5739,15 +5737,26 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_USER_SETTINGS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - menu_displaylist_parse_settings_enum(menu, info, + + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_ACCOUNTS_LIST, - PARSE_ACTION, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ACTION, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY_NICKNAME, - PARSE_ONLY_STRING, false); - menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_STRING, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_USER_LANGUAGE, - PARSE_ONLY_UINT, false); + PARSE_ONLY_UINT, false) == 0) + count++; + + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); info->need_refresh = true; info->need_push = true; @@ -6199,14 +6208,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_ONSCREEN_DISPLAY_SETTINGS, PARSE_ACTION, false); ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_USER_INTERFACE_SETTINGS, PARSE_ACTION, false); -#ifdef HAVE_CHEEVOS ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS, PARSE_ACTION, false); -#endif -#ifdef HAVE_LAKKA ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_WIFI_SETTINGS, PARSE_ACTION, false); -#endif ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETWORK_SETTINGS, PARSE_ACTION, false); ret = menu_displaylist_parse_settings_enum(menu, info, @@ -6500,7 +6505,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_ADD_CONTENT_LIST, PARSE_ACTION, false); -#ifdef HAVE_NETWORKING if (settings->bools.menu_content_show_netplay) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETPLAY, @@ -6509,18 +6513,15 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_ONLINE_UPDATER, PARSE_ACTION, false); -#endif menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_SETTINGS, PARSE_ACTION, false); if (settings->bools.menu_show_information) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INFORMATION_LIST, PARSE_ACTION, false); -#ifndef HAVE_DYNAMIC menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_RESTART_RETROARCH, PARSE_ACTION, false); -#endif if (settings->bools.menu_show_configurations) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CONFIGURATIONS_LIST, @@ -6533,7 +6534,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_QUIT_RETROARCH, PARSE_ACTION, false); -#if defined(HAVE_LAKKA) if (settings->bools.menu_show_reboot) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_REBOOT, @@ -6541,7 +6541,6 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_SHUTDOWN, PARSE_ACTION, false); -#endif info->need_push = true; } break; @@ -6643,39 +6642,43 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) break; case DISPLAYLIST_ACCOUNTS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); -#ifdef HAVE_CHEEVOS - ret = menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_ACCOUNTS_RETRO_ACHIEVEMENTS, - PARSE_ACTION, false); -#else - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ITEMS), - msg_hash_to_str(MENU_ENUM_LABEL_NO_ITEMS), - MENU_ENUM_LABEL_NO_ITEMS, - MENU_SETTING_NO_ITEM, 0, 0); - ret = 0; -#endif + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_ACCOUNTS_RETRO_ACHIEVEMENTS, + PARSE_ACTION, false) == 0) + count++; + + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ITEMS), + msg_hash_to_str(MENU_ENUM_LABEL_NO_ITEMS), + MENU_ENUM_LABEL_NO_ITEMS, + MENU_SETTING_NO_ITEM, 0, 0); + + ret = 0; info->need_refresh = true; info->need_push = true; break; case DISPLAYLIST_ACCOUNTS_CHEEVOS_LIST: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); -#ifdef HAVE_CHEEVOS - ret = menu_displaylist_parse_settings_enum(menu, info, + + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CHEEVOS_USERNAME, - PARSE_ONLY_STRING, false); - ret = menu_displaylist_parse_settings_enum(menu, info, + PARSE_ONLY_STRING, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CHEEVOS_PASSWORD, - PARSE_ONLY_STRING, false); -#else - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ITEMS), - msg_hash_to_str(MENU_ENUM_LABEL_NO_ITEMS), - MENU_ENUM_LABEL_NO_ITEMS, - MENU_SETTING_NO_ITEM, 0, 0); - ret = 0; -#endif + PARSE_ONLY_STRING, false) == 0) + count++; + + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ITEMS), + msg_hash_to_str(MENU_ENUM_LABEL_NO_ITEMS), + MENU_ENUM_LABEL_NO_ITEMS, + MENU_SETTING_NO_ITEM, 0, 0); + + ret = 0; info->need_refresh = true; info->need_push = true; break; @@ -6760,16 +6763,19 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) free(info->exts); if (info->path) free(info->path); - info->exts = strdup("dbc"); - info->path = strdup(settings->paths.directory_cursor); + info->exts = strdup("dbc"); + info->path = strdup(settings->paths.directory_cursor); break; case DISPLAYLIST_CONFIG_FILES: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); filebrowser_clear_type(); + info->type_default = FILE_TYPE_CONFIG; + if (!string_is_empty(info->exts)) free(info->exts); - info->exts = strdup("cfg"); + + info->exts = strdup("cfg"); load_content = false; use_filebrowser = true; break; @@ -6858,7 +6864,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) use_filebrowser = true; if (!string_is_empty(info->exts)) free(info->exts); - info->exts = strdup("filt"); + info->exts = strdup("filt"); break; case DISPLAYLIST_IMAGES: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); @@ -6940,7 +6946,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) use_filebrowser = true; if (!string_is_empty(info->exts)) free(info->exts); - info->exts = strdup("dsp"); + info->exts = strdup("dsp"); break; case DISPLAYLIST_CHEAT_FILES: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); @@ -6950,7 +6956,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) use_filebrowser = true; if (!string_is_empty(info->exts)) free(info->exts); - info->exts = strdup("cht"); + info->exts = strdup("cht"); break; case DISPLAYLIST_CONTENT_HISTORY: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); @@ -6959,7 +6965,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) use_filebrowser = true; if (!string_is_empty(info->exts)) free(info->exts); - info->exts = strdup("lpl"); + info->exts = strdup("lpl"); break; case DISPLAYLIST_FONTS: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); @@ -6969,7 +6975,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) use_filebrowser = true; if (!string_is_empty(info->exts)) free(info->exts); - info->exts = strdup("ttf"); + info->exts = strdup("ttf"); break; case DISPLAYLIST_OVERLAYS: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); @@ -6979,7 +6985,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) use_filebrowser = true; if (!string_is_empty(info->exts)) free(info->exts); - info->exts = strdup("cfg"); + info->exts = strdup("cfg"); break; case DISPLAYLIST_RECORD_CONFIG_FILES: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); @@ -6989,17 +6995,17 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) use_filebrowser = true; if (!string_is_empty(info->exts)) free(info->exts); - info->exts = strdup("cfg"); + info->exts = strdup("cfg"); break; case DISPLAYLIST_REMAP_FILES: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); filebrowser_clear_type(); - info->type_default = FILE_TYPE_REMAP; - load_content = false; - use_filebrowser = true; + info->type_default = FILE_TYPE_REMAP; + load_content = false; + use_filebrowser = true; if (!string_is_empty(info->exts)) free(info->exts); - info->exts = strdup("rmp"); + info->exts = strdup("rmp"); break; case DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 803540ee3c..38f3d7c406 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -2108,7 +2108,7 @@ static bool setting_append_list( &subgroup_info, parent_group); -#ifndef __CELLOS_LV2__ +#if !defined(__CELLOS_LV2__) && !defined(HAVE_DYNAMIC) CONFIG_ACTION( list, list_info, MENU_ENUM_LABEL_RESTART_RETROARCH, @@ -2392,6 +2392,7 @@ static bool setting_append_list( parent_group); settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); +#ifdef HAVE_CHEEVOS CONFIG_ACTION( list, list_info, MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS, @@ -2399,6 +2400,7 @@ static bool setting_append_list( &group_info, &subgroup_info, parent_group); +#endif CONFIG_ACTION( list, list_info, @@ -2409,6 +2411,7 @@ static bool setting_append_list( parent_group); settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED); +#ifdef HAVE_LAKKA if (string_is_not_equal(settings->arrays.wifi_driver, "null")) { CONFIG_ACTION( @@ -2419,6 +2422,7 @@ static bool setting_append_list( &subgroup_info, parent_group); } +#endif CONFIG_ACTION( list, list_info, From a75e7ede0d3d7f772b8c590502afce9f6d2a6d1b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 14:47:21 +0200 Subject: [PATCH 452/517] (D3D9) Implement Menu Linear Filter --- gfx/drivers/d3d9.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index 1f66f5b1d7..a06452f309 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -413,8 +413,9 @@ static void d3d9_set_mvp(void *data, static void d3d9_overlay_render(d3d_video_t *d3d, video_frame_info_t *video_info, - overlay_t *overlay) + overlay_t *overlay, bool force_linear) { + D3DTEXTUREFILTERTYPE filter_type; LPDIRECT3DVERTEXDECLARATION9 vertex_decl; struct video_viewport vp; void *verts; @@ -503,12 +504,21 @@ static void d3d9_overlay_render(d3d_video_t *d3d, d3d9_set_viewports(d3d->dev, &vp_full); } + filter_type = D3DTEXF_LINEAR; + + if (!force_linear) + { + settings_t *settings = config_get_ptr(); + if (!settings->bools.menu_linear_filter) + filter_type = D3DTEXF_POINT; + } + /* Render overlay. */ d3d9_set_texture(d3d->dev, 0, overlay->tex); d3d9_set_sampler_address_u(d3d->dev, 0, D3DTADDRESS_BORDER); d3d9_set_sampler_address_v(d3d->dev, 0, D3DTADDRESS_BORDER); - d3d9_set_sampler_minfilter(d3d->dev, 0, D3DTEXF_LINEAR); - d3d9_set_sampler_magfilter(d3d->dev, 0, D3DTEXF_LINEAR); + d3d9_set_sampler_minfilter(d3d->dev, 0, filter_type); + d3d9_set_sampler_magfilter(d3d->dev, 0, filter_type); d3d9_draw_primitive(d3d->dev, D3DPT_TRIANGLESTRIP, 0, 2); /* Restore previous state. */ @@ -1592,7 +1602,7 @@ static bool d3d9_frame(void *data, const void *frame, if (d3d->menu && d3d->menu->enabled) { d3d9_set_mvp(d3d, NULL, &d3d->mvp); - d3d9_overlay_render(d3d, video_info, d3d->menu); + d3d9_overlay_render(d3d, video_info, d3d->menu, false); d3d->menu_display.offset = 0; d3d9_set_vertex_declaration(d3d->dev, d3d->menu_display.decl); @@ -1622,7 +1632,7 @@ static bool d3d9_frame(void *data, const void *frame, { d3d9_set_mvp(d3d, NULL, &d3d->mvp); for (i = 0; i < d3d->overlays_size; i++) - d3d9_overlay_render(d3d, video_info, &d3d->overlays[i]); + d3d9_overlay_render(d3d, video_info, &d3d->overlays[i], true); } #endif From c506f489df634e18f0561a0f128402cd5da4c4a9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 14:49:58 +0200 Subject: [PATCH 453/517] (D3D8) Hook up Menu Linear Filter --- gfx/drivers/d3d8.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index 94fc4f7269..f21c2cc3c2 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -439,14 +439,14 @@ static void d3d8_set_mvp(void *data, static void d3d8_overlay_render(d3d_video_t *d3d, video_frame_info_t *video_info, - overlay_t *overlay) + overlay_t *overlay, bool force_linear) { D3DVIEWPORT8 vp_full; + D3DTEXTUREFILTERTYPE filter_type; struct video_viewport vp; void *verts; unsigned i; Vertex vert[4]; - unsigned width = video_info->width; unsigned height = video_info->height; @@ -514,12 +514,19 @@ static void d3d8_overlay_render(d3d_video_t *d3d, d3d8_set_viewports(d3d->dev, &vp_full); } + if (!force_linear) + { + settings_t *settings = config_get_ptr(); + if (!settings->bools.menu_linear_filter) + filter_type = D3DTEXF_POINT; + } + /* Render overlay. */ d3d8_set_texture(d3d->dev, 0, overlay->tex); d3d8_set_sampler_address_u(d3d->dev, 0, D3DTADDRESS_BORDER); d3d8_set_sampler_address_v(d3d->dev, 0, D3DTADDRESS_BORDER); - d3d8_set_sampler_minfilter(d3d->dev, 0, D3DTEXF_LINEAR); - d3d8_set_sampler_magfilter(d3d->dev, 0, D3DTEXF_LINEAR); + d3d8_set_sampler_minfilter(d3d->dev, 0, filter_type); + d3d8_set_sampler_magfilter(d3d->dev, 0, filter_type); d3d8_draw_primitive(d3d->dev, D3DPT_TRIANGLESTRIP, 0, 2); /* Restore previous state. */ @@ -1616,7 +1623,7 @@ static bool d3d8_frame(void *data, const void *frame, if (d3d->menu && d3d->menu->enabled) { d3d8_set_mvp(d3d, NULL, &d3d->mvp); - d3d8_overlay_render(d3d, video_info, d3d->menu); + d3d8_overlay_render(d3d, video_info, d3d->menu, false); d3d->menu_display.offset = 0; d3d8_set_stream_source(d3d->dev, 0, d3d->menu_display.buffer, 0, sizeof(Vertex)); @@ -1642,7 +1649,7 @@ static bool d3d8_frame(void *data, const void *frame, { d3d8_set_mvp(d3d, NULL, &d3d->mvp); for (i = 0; i < d3d->overlays_size; i++) - d3d8_overlay_render(d3d, video_info, &d3d->overlays[i]); + d3d8_overlay_render(d3d, video_info, &d3d->overlays[i], true); } #endif From 6b31bd8b48241bcb9a360e02dd255ba406165afe Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 15:14:06 +0200 Subject: [PATCH 454/517] Only show Menu Linear Filter for RGUI and only show it for video drivers that implement it --- gfx/drivers/d3d10.c | 11 ++++++++++- gfx/drivers/d3d11.c | 11 ++++++++++- gfx/drivers/d3d12.c | 11 ++++++++++- gfx/drivers/d3d8.c | 1 + gfx/drivers/d3d9.c | 1 + gfx/drivers/gl.c | 1 + gfx/video_driver.h | 3 ++- menu/menu_setting.c | 47 ++++++++++++++++++++++++++++++--------------- 8 files changed, 67 insertions(+), 19 deletions(-) diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 1e09d00a2b..ea557a2084 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -1583,8 +1583,17 @@ d3d10_get_hw_render_interface(void* data, const struct retro_hw_render_interface } #endif +static uint32_t d3d10_get_flags(void *data) +{ + uint32_t flags = 0; + + BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING); + + return flags; +} + static const video_poke_interface_t d3d10_poke_interface = { - NULL, /* get_flags */ + d3d10_get_flags, NULL, /* set_coords */ NULL, /* set_mvp */ d3d10_gfx_load_texture, diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index f90b7bc532..e9fc4b71f1 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -1604,8 +1604,17 @@ d3d11_get_hw_render_interface(void* data, const struct retro_hw_render_interface return d3d11->hw.enable; } +static uint32_t d3d11_get_flags(void *data) +{ + uint32_t flags = 0; + + BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING); + + return flags; +} + static const video_poke_interface_t d3d11_poke_interface = { - NULL, /* get_flags */ + d3d11_get_flags, NULL, /* set_coords */ NULL, /* set_mvp */ d3d11_gfx_load_texture, diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 5474cea885..4f7e884c0c 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -1750,8 +1750,17 @@ static void d3d12_gfx_unload_texture(void* data, uintptr_t handle) free(texture); } +static uint32_t d3d12_get_flags(void *data) +{ + uint32_t flags = 0; + + BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING); + + return flags; +} + static const video_poke_interface_t d3d12_poke_interface = { - NULL, /* get_flags */ + d3d12_get_flags, NULL, /* set_coords */ NULL, /* set_mvp */ d3d12_gfx_load_texture, diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index f21c2cc3c2..c39a70cd3c 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -1872,6 +1872,7 @@ static uint32_t d3d8_get_flags(void *data) uint32_t flags = 0; BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); + BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING); return flags; } diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index a06452f309..5902a65612 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -1899,6 +1899,7 @@ static uint32_t d3d9_get_flags(void *data) uint32_t flags = 0; BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); + BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING); return flags; } diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 0cf00d7a7a..26335bd7d2 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2608,6 +2608,7 @@ static uint32_t gl_get_flags(void *data) BIT32_SET(flags, GFX_CTX_FLAGS_HARD_SYNC); BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); + BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING); return flags; } diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 90a93e6794..4563e9a3b7 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -115,7 +115,8 @@ enum display_flags GFX_CTX_FLAGS_MULTISAMPLING, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES, GFX_CTX_FLAGS_HARD_SYNC, - GFX_CTX_FLAGS_BLACK_FRAME_INSERTION + GFX_CTX_FLAGS_BLACK_FRAME_INSERTION, + GFX_CTX_FLAGS_MENU_FRAME_FILTERING }; enum shader_uniform_type diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 38f3d7c406..e307387d48 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5166,21 +5166,38 @@ static bool setting_append_list( SD_FLAG_ADVANCED ); - CONFIG_BOOL( - list, list_info, - &settings->bools.menu_linear_filter, - MENU_ENUM_LABEL_MENU_LINEAR_FILTER, - MENU_ENUM_LABEL_VALUE_MENU_LINEAR_FILTER, - true, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler, - SD_FLAG_ADVANCED - ); + if (string_is_equal(settings->arrays.menu_driver, "rgui")) + { + gfx_ctx_flags_t flags; + bool setting_set = false; + + if (video_driver_get_flags(&flags)) + if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING)) + setting_set = true; + + flags.flags = 0; + + if (video_context_driver_get_flags(&flags)) + if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING)) + setting_set = true; + + if (setting_set) + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_linear_filter, + MENU_ENUM_LABEL_MENU_LINEAR_FILTER, + MENU_ENUM_LABEL_VALUE_MENU_LINEAR_FILTER, + true, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_ADVANCED + ); + } if (string_is_equal(settings->arrays.menu_driver, "xmb")) { From 68c9cf4cb9a84983fdf6b831d57e7dddf25b48db Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 15:27:12 +0200 Subject: [PATCH 455/517] update CHANGES.md --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1a077853d0..5f6eae415a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,9 @@ with D3D11 driver except for there being no hardware rendering right now. - LOCALIZATION: Update Italian translation. - LOCALIZATION: Update Polish translation. - MENU: Add Rewind/Latency/Overlay settings to Quick Menu +- MENU/RGUI: Only show Menu Linear Filter for RGUI and only show it for +video drivers that implement it (D3D8/9/10/11/12/GL) +- MENU/RGUI: D3D8/D3D9: Hookup Menu Linear Filter - MENU/XMB: Disable XMB shadow icons by default for PowerPC and ARM for performance reasons. - MENU/XMB: Left/right thumbnails are now automatically scaled according to layout. - MENU/XMB: Add Left Thumbnails (additional to the right). From f6b33b1d30d42c9ff69a7ec700d0afa4b8d4b353 Mon Sep 17 00:00:00 2001 From: gblues Date: Tue, 24 Apr 2018 06:37:02 -0700 Subject: [PATCH 456/517] missing file --- input/common/input_common.c | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 input/common/input_common.c diff --git a/input/common/input_common.c b/input/common/input_common.c new file mode 100644 index 0000000000..4f6e656363 --- /dev/null +++ b/input/common/input_common.c @@ -0,0 +1,78 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "../include/gamepad.h" + +enum pad_axes { + AXIS_LEFT_ANALOG_X, + AXIS_LEFT_ANALOG_Y, + AXIS_RIGHT_ANALOG_X, + AXIS_RIGHT_ANALOG_Y, + AXIS_INVALID +}; + +static int16_t clamp_axis(int16_t value, bool is_negative) +{ + if(is_negative && value > 0) + return 0; + if(!is_negative && value < 0) + return 0; + + return value; +} + +void gamepad_read_axis_data(uint32_t axis, axis_data *data) +{ + if(!data) + return; + + data->axis = AXIS_POS_GET(axis); + data->is_negative = false; + + if(data->axis >= AXIS_INVALID) + { + data->axis = AXIS_NEG_GET(axis); + data->is_negative = true; + } +} + +int16_t gamepad_get_axis_value(int16_t state[3][2], axis_data *data) +{ + int16_t value = 0; + + if(!data) + return 0; + + switch(data->axis) + { + case AXIS_LEFT_ANALOG_X: + value = state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_X]; + break; + case AXIS_LEFT_ANALOG_Y: + value = state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_Y]; + break; + case AXIS_RIGHT_ANALOG_X: + value = state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X]; + break; + case AXIS_RIGHT_ANALOG_Y: + value = state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y]; + break; + } + + return clamp_axis(value, data->is_negative); +} + + From ee421fcaa8a59cd04afcc98dd6743ae28d9fd78b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 16:01:51 +0200 Subject: [PATCH 457/517] Add RGUI Border Filler Enable --- configuration.c | 3 +++ configuration.h | 1 + intl/msg_hash_ar.h | 2 ++ intl/msg_hash_chs.h | 2 ++ intl/msg_hash_cht.h | 2 ++ intl/msg_hash_de.h | 2 ++ intl/msg_hash_eo.h | 2 ++ intl/msg_hash_es.h | 2 ++ intl/msg_hash_fr.h | 2 ++ intl/msg_hash_it.h | 2 ++ intl/msg_hash_ja.h | 2 ++ intl/msg_hash_ko.h | 2 ++ intl/msg_hash_lbl.h | 2 ++ intl/msg_hash_nl.h | 2 ++ intl/msg_hash_pl.h | 2 ++ intl/msg_hash_pt_br.h | 2 ++ intl/msg_hash_pt_pt.h | 2 ++ intl/msg_hash_ru.h | 2 ++ intl/msg_hash_us.h | 2 ++ intl/msg_hash_vn.h | 2 ++ menu/drivers/rgui.c | 41 +++++++++++++++++++++++++---------------- menu/menu_displaylist.c | 4 ++++ menu/menu_setting.c | 16 ++++++++++++++++ msg_hash.h | 1 + 24 files changed, 86 insertions(+), 16 deletions(-) diff --git a/configuration.c b/configuration.c index bfd2615f37..522bcef290 100644 --- a/configuration.c +++ b/configuration.c @@ -1331,6 +1331,9 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, #ifdef HAVE_MATERIALUI SETTING_BOOL("materialui_icons_enable", &settings->bools.menu_materialui_icons_enable, true, materialui_icons_enable, false); #endif +#ifdef HAVE_RGUI + SETTING_BOOL("rgui_border_filler_enable", &settings->bools.menu_rgui_border_filler_enable, true, true, false); +#endif #ifdef HAVE_XMB SETTING_BOOL("xmb_shadows_enable", &settings->bools.menu_xmb_shadows_enable, true, xmb_shadows_enable, false); SETTING_BOOL("xmb_vertical_thumbnails", &settings->bools.menu_xmb_vertical_thumbnails, true, xmb_vertical_thumbnails, false); diff --git a/configuration.h b/configuration.h index 890973086c..97fb7249c9 100644 --- a/configuration.h +++ b/configuration.h @@ -148,6 +148,7 @@ typedef struct settings bool menu_show_quit_retroarch; bool menu_show_reboot; bool menu_materialui_icons_enable; + bool menu_rgui_border_filler_enable; bool menu_xmb_shadows_enable; bool menu_xmb_vertical_thumbnails; bool menu_content_show_settings; diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index ac772516dc..ecb9d5c5d6 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -3435,3 +3435,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index 6e6f0155a8..c7ed79c00a 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -3221,3 +3221,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index 695095e7bd..bd29173ff5 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -3213,3 +3213,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_de.h b/intl/msg_hash_de.h index 1c7c9afee0..eaf90f6b40 100644 --- a/intl/msg_hash_de.h +++ b/intl/msg_hash_de.h @@ -3327,3 +3327,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index b2e52d50ee..018e802a54 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -3086,3 +3086,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_es.h b/intl/msg_hash_es.h index 637a8179ec..7633418c99 100644 --- a/intl/msg_hash_es.h +++ b/intl/msg_hash_es.h @@ -5817,3 +5817,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index 12c5f97c06..65f57c3dcc 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -3251,3 +3251,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index fcf6b070bf..dbc83f8fb7 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -3309,3 +3309,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Mostra statistiche") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Mostra statistiche tecniche su schermo.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index 3562981f0b..635c8ae8ec 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -3325,3 +3325,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_ko.h b/intl/msg_hash_ko.h index 875cbbf201..e36cbd02a5 100644 --- a/intl/msg_hash_ko.h +++ b/intl/msg_hash_ko.h @@ -3212,3 +3212,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index cd2485fafa..c463f48355 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1473,3 +1473,5 @@ MSG_HASH(MENU_ENUM_LABEL_INPUT_DRIVER_LINUXRAW, "linuxraw") MSG_HASH(MENU_ENUM_LABEL_VIDEO_WINDOW_SHOW_DECORATIONS, "video_window_show_decorations") +MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_BORDER_FILLER_ENABLE, + "menu_rgui_border_filler_enable") diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index 3501262f53..e05d9964eb 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -3088,3 +3088,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index b43111f30e..56575412e9 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -3447,3 +3447,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Wyświetl statystyki") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Pokaż techniczne statystyki na ekranie.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index f819763186..c8c88335b2 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -4316,3 +4316,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Exibir estatísticas") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Mostrar estatísticas técnicas na tela.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_pt_pt.h b/intl/msg_hash_pt_pt.h index ca37f48512..2e8fee43ba 100644 --- a/intl/msg_hash_pt_pt.h +++ b/intl/msg_hash_pt_pt.h @@ -3186,3 +3186,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_ru.h b/intl/msg_hash_ru.h index 9f045d4b3d..7807f3856c 100644 --- a/intl/msg_hash_ru.h +++ b/intl/msg_hash_ru.h @@ -3270,3 +3270,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 02a1eee6b4..0884f06788 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3491,3 +3491,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index 971c4408cd..25fb749e51 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -3243,3 +3243,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_STATISTICS_SHOW, "Display Statistics") MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + "Enable border filler") diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 16de9c2c43..8ea1841554 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -263,12 +263,17 @@ static void rgui_render_background(void) if (rgui_framebuf_data) { - rgui_fill_rect(rgui_framebuf_data, fb_pitch, 5, 5, fb_width - 10, 5, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, fb_pitch, 5, fb_height - 10, fb_width - 10, 5, rgui_green_filler); + settings_t *settings = config_get_ptr(); - rgui_fill_rect(rgui_framebuf_data, fb_pitch, 5, 5, 5, fb_height - 10, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, fb_pitch, fb_width - 10, 5, 5, fb_height - 10, - rgui_green_filler); + if (settings->bools.menu_rgui_border_filler_enable) + { + rgui_fill_rect(rgui_framebuf_data, fb_pitch, 5, 5, fb_width - 10, 5, rgui_green_filler); + rgui_fill_rect(rgui_framebuf_data, fb_pitch, 5, fb_height - 10, fb_width - 10, 5, rgui_green_filler); + + rgui_fill_rect(rgui_framebuf_data, fb_pitch, 5, 5, 5, fb_height - 10, rgui_green_filler); + rgui_fill_rect(rgui_framebuf_data, fb_pitch, fb_width - 10, 5, 5, fb_height - 10, + rgui_green_filler); + } } } @@ -341,17 +346,21 @@ static void rgui_render_messagebox(const char *message) rgui_fill_rect(rgui_framebuf_data, fb_pitch, x + 5, y + 5, width - 10, height - 10, rgui_gray_filler); - rgui_fill_rect(rgui_framebuf_data, - fb_pitch, x, y, width - 5, 5, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, - fb_pitch, x + width - 5, y, 5, - height - 5, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, - fb_pitch, x + 5, y + height - 5, - width - 5, 5, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, - fb_pitch, x, y + 5, 5, - height - 5, rgui_green_filler); + + if (settings->bools.menu_rgui_border_filler_enable) + { + rgui_fill_rect(rgui_framebuf_data, + fb_pitch, x, y, width - 5, 5, rgui_green_filler); + rgui_fill_rect(rgui_framebuf_data, + fb_pitch, x + width - 5, y, 5, + height - 5, rgui_green_filler); + rgui_fill_rect(rgui_framebuf_data, + fb_pitch, x + 5, y + height - 5, + width - 5, 5, rgui_green_filler); + rgui_fill_rect(rgui_framebuf_data, + fb_pitch, x, y + 5, 5, + height - 5, rgui_green_filler); + } } color = NORMAL_COLOR(settings); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 1ea2c825a6..dd89b20305 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5306,6 +5306,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_MENU_FRAMEBUFFER_OPACITY, PARSE_ONLY_FLOAT, false) == 0) count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_RGUI_BORDER_FILLER_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_LINEAR_FILTER, PARSE_ONLY_BOOL, false) == 0) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index e307387d48..22c719367f 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5181,6 +5181,22 @@ static bool setting_append_list( if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING)) setting_set = true; + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_rgui_border_filler_enable, + MENU_ENUM_LABEL_MENU_RGUI_BORDER_FILLER_ENABLE, + MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, + true, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_ADVANCED + ); + if (setting_set) CONFIG_BOOL( list, list_info, diff --git a/msg_hash.h b/msg_hash.h index c5151f1314..5abcdb94d7 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -748,6 +748,7 @@ enum msg_hash_enums MENU_LABEL(PAUSE_NONACTIVE), MENU_LABEL(MOUSE_ENABLE), MENU_LABEL(POINTER_ENABLE), + MENU_LABEL(MENU_RGUI_BORDER_FILLER_ENABLE), MENU_LABEL(MENU_LINEAR_FILTER), MENU_LABEL(MENU_HORIZONTAL_ANIMATION), MENU_LABEL(NAVIGATION_WRAPAROUND), From 65fe25f03c0d369a74f8888789c24f418d48e28a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 16:45:06 +0200 Subject: [PATCH 458/517] (RGUI) Add background border filler thickness enable --- configuration.c | 2 ++ configuration.h | 2 ++ intl/msg_hash_ar.h | 4 +++ intl/msg_hash_chs.h | 4 +++ intl/msg_hash_cht.h | 4 +++ intl/msg_hash_de.h | 4 +++ intl/msg_hash_eo.h | 4 +++ intl/msg_hash_es.h | 4 +++ intl/msg_hash_fr.h | 4 +++ intl/msg_hash_it.h | 4 +++ intl/msg_hash_ja.h | 4 +++ intl/msg_hash_ko.h | 4 +++ intl/msg_hash_lbl.h | 4 +++ intl/msg_hash_nl.h | 4 +++ intl/msg_hash_pl.h | 4 +++ intl/msg_hash_pt_br.h | 4 +++ intl/msg_hash_pt_pt.h | 4 +++ intl/msg_hash_ru.h | 4 +++ intl/msg_hash_us.h | 4 +++ intl/msg_hash_vn.h | 4 +++ menu/drivers/rgui.c | 73 +++++++++++++++++++++++++---------------- menu/menu_displaylist.c | 8 +++++ menu/menu_setting.c | 36 ++++++++++++++++++-- msg_hash.h | 2 ++ 24 files changed, 165 insertions(+), 30 deletions(-) diff --git a/configuration.c b/configuration.c index 522bcef290..b258632f7d 100644 --- a/configuration.c +++ b/configuration.c @@ -1332,6 +1332,8 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("materialui_icons_enable", &settings->bools.menu_materialui_icons_enable, true, materialui_icons_enable, false); #endif #ifdef HAVE_RGUI + SETTING_BOOL("rgui_background_filler_thickness_enable", &settings->bools.menu_rgui_background_filler_thickness_enable, true, true, false); + SETTING_BOOL("rgui_border_filler_thickness_enable", &settings->bools.menu_rgui_border_filler_thickness_enable, true, true, false); SETTING_BOOL("rgui_border_filler_enable", &settings->bools.menu_rgui_border_filler_enable, true, true, false); #endif #ifdef HAVE_XMB diff --git a/configuration.h b/configuration.h index 97fb7249c9..eca18e512d 100644 --- a/configuration.h +++ b/configuration.h @@ -148,6 +148,8 @@ typedef struct settings bool menu_show_quit_retroarch; bool menu_show_reboot; bool menu_materialui_icons_enable; + bool menu_rgui_background_filler_thickness_enable; + bool menu_rgui_border_filler_thickness_enable; bool menu_rgui_border_filler_enable; bool menu_xmb_shadows_enable; bool menu_xmb_vertical_thumbnails; diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index ecb9d5c5d6..15edc43cdb 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -3437,3 +3437,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index c7ed79c00a..0261fd48a7 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -3223,3 +3223,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index bd29173ff5..52b50d9ba8 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -3215,3 +3215,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_de.h b/intl/msg_hash_de.h index eaf90f6b40..bd052b7d18 100644 --- a/intl/msg_hash_de.h +++ b/intl/msg_hash_de.h @@ -3329,3 +3329,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 018e802a54..9be34208be 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -3088,3 +3088,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_es.h b/intl/msg_hash_es.h index 7633418c99..6132592fe1 100644 --- a/intl/msg_hash_es.h +++ b/intl/msg_hash_es.h @@ -5819,3 +5819,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index 65f57c3dcc..b5133d4f0d 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -3253,3 +3253,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index dbc83f8fb7..7268c142e6 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -3311,3 +3311,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Mostra statistiche tecniche su schermo.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index 635c8ae8ec..53db7a8bf5 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -3327,3 +3327,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_ko.h b/intl/msg_hash_ko.h index e36cbd02a5..a32764fefc 100644 --- a/intl/msg_hash_ko.h +++ b/intl/msg_hash_ko.h @@ -3214,3 +3214,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index c463f48355..a53cd5d6e2 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1475,3 +1475,7 @@ MSG_HASH(MENU_ENUM_LABEL_VIDEO_WINDOW_SHOW_DECORATIONS, "video_window_show_decorations") MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_BORDER_FILLER_ENABLE, "menu_rgui_border_filler_enable") +MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "menu_rgui_border_filler_thickness_enable") +MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "menu_rgui_background_filler_thickness_enable") diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index e05d9964eb..fd2f1a6e72 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -3090,3 +3090,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index 56575412e9..8c3d081e69 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -3449,3 +3449,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Pokaż techniczne statystyki na ekranie.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index c8c88335b2..69e60613c8 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -4318,3 +4318,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Mostrar estatísticas técnicas na tela.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_pt_pt.h b/intl/msg_hash_pt_pt.h index 2e8fee43ba..dc0d414f01 100644 --- a/intl/msg_hash_pt_pt.h +++ b/intl/msg_hash_pt_pt.h @@ -3188,3 +3188,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_ru.h b/intl/msg_hash_ru.h index 7807f3856c..f3956f3c14 100644 --- a/intl/msg_hash_ru.h +++ b/intl/msg_hash_ru.h @@ -3272,3 +3272,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 0884f06788..87fb91209c 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3493,3 +3493,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index 25fb749e51..e8ad2f7d60 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -3245,3 +3245,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_STATISTICS_SHOW, "Show onscreen technical statistics.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_ENABLE, "Enable border filler") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + "Enable border filler thickness") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + "Enable background filler thickness") diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 8ea1841554..4c7beca70b 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -55,11 +55,14 @@ typedef struct { + bool bg_modified; bool force_redraw; bool mouse_show; unsigned last_width; unsigned last_height; unsigned frame_count; + bool bg_thickness; + bool border_thickness; float scroll_y; char *msgbox; } rgui_t; @@ -86,10 +89,10 @@ static uint16_t argb32_to_rgba4444(uint32_t col) #endif -static uint16_t rgui_gray_filler(unsigned x, unsigned y) +static uint16_t rgui_gray_filler(rgui_t *rgui, unsigned x, unsigned y) { - unsigned col = (((x >> 1) + (y >> 1)) & 1) + 1; - + unsigned shft = (rgui->bg_thickness ? 1 : 0); + unsigned col = (((x >> shft) + (y >> shft)) & 1) + 1; #if defined(GEKKO) || defined(PSP) return (6 << 12) | (col << 8) | (col << 4) | (col << 0); #else @@ -97,9 +100,10 @@ static uint16_t rgui_gray_filler(unsigned x, unsigned y) #endif } -static uint16_t rgui_green_filler(unsigned x, unsigned y) +static uint16_t rgui_green_filler(rgui_t *rgui, unsigned x, unsigned y) { - unsigned col = (((x >> 1) + (y >> 1)) & 1) + 1; + unsigned shft = (rgui->border_thickness ? 1 : 0); + unsigned col = (((x >> shft) + (y >> shft)) & 1) + 1; #if defined(GEKKO) || defined(PSP) return (6 << 12) | (col << 8) | (col << 5) | (col << 0); #else @@ -108,17 +112,18 @@ static uint16_t rgui_green_filler(unsigned x, unsigned y) } static void rgui_fill_rect( + rgui_t *rgui, uint16_t *data, size_t pitch, unsigned x, unsigned y, unsigned width, unsigned height, - uint16_t (*col)(unsigned x, unsigned y)) + uint16_t (*col)(rgui_t *rgui, unsigned x, unsigned y)) { unsigned i, j; for (j = y; j < y + height; j++) for (i = x; i < x + width; i++) - data[j * (pitch >> 1) + i] = col(i, j); + data[j * (pitch >> 1) + i] = col(rgui, i, j); } static void rgui_color_rect( @@ -239,7 +244,7 @@ static bool rguidisp_init_font(menu_handle_t *menu) return true; } -static void rgui_render_background(void) +static void rgui_render_background(rgui_t *rgui) { size_t pitch_in_pixels, size; size_t fb_pitch; @@ -267,11 +272,11 @@ static void rgui_render_background(void) if (settings->bools.menu_rgui_border_filler_enable) { - rgui_fill_rect(rgui_framebuf_data, fb_pitch, 5, 5, fb_width - 10, 5, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, fb_pitch, 5, fb_height - 10, fb_width - 10, 5, rgui_green_filler); + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, 5, 5, fb_width - 10, 5, rgui_green_filler); + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, 5, fb_height - 10, fb_width - 10, 5, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, fb_pitch, 5, 5, 5, fb_height - 10, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, fb_pitch, fb_width - 10, 5, 5, fb_height - 10, + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, 5, 5, 5, fb_height - 10, rgui_green_filler); + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, fb_width - 10, 5, 5, fb_height - 10, rgui_green_filler); } } @@ -290,7 +295,7 @@ static void rgui_set_message(void *data, const char *message) rgui->force_redraw = true; } -static void rgui_render_messagebox(const char *message) +static void rgui_render_messagebox(rgui_t *rgui, const char *message) { int x, y; uint16_t color; @@ -343,21 +348,21 @@ static void rgui_render_messagebox(const char *message) if (rgui_framebuf_data) { - rgui_fill_rect(rgui_framebuf_data, + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, x + 5, y + 5, width - 10, height - 10, rgui_gray_filler); if (settings->bools.menu_rgui_border_filler_enable) { - rgui_fill_rect(rgui_framebuf_data, + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, x, y, width - 5, 5, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, x + width - 5, y, 5, height - 5, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, x + 5, y + height - 5, width - 5, 5, rgui_green_filler); - rgui_fill_rect(rgui_framebuf_data, + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, x, y + 5, 5, height - 5, rgui_green_filler); } @@ -399,6 +404,15 @@ static void rgui_blit_cursor(void) static void rgui_frame(void *data, video_frame_info_t *video_info) { rgui_t *rgui = (rgui_t*)data; + settings_t *settings = config_get_ptr(); + + if ((settings->bools.menu_rgui_background_filler_thickness_enable != rgui->bg_thickness) || + (settings->bools.menu_rgui_border_filler_thickness_enable != rgui->border_thickness) + ) + rgui->bg_modified = true; + + rgui->bg_thickness = settings->bools.menu_rgui_background_filler_thickness_enable; + rgui->border_thickness = settings->bools.menu_rgui_border_filler_thickness_enable; rgui->frame_count++; } @@ -439,14 +453,17 @@ static void rgui_render(void *data, bool is_idle) &fb_pitch); /* if the framebuffer changed size, recache the background */ - if (rgui->last_width != fb_width || rgui->last_height != fb_height) + if (rgui->bg_modified || rgui->last_width != fb_width || rgui->last_height != fb_height) { if (rgui_framebuf_data) - rgui_fill_rect(rgui_framebuf_data, + rgui_fill_rect(rgui, rgui_framebuf_data, fb_pitch, 0, fb_height, fb_width, 4, rgui_gray_filler); rgui->last_width = fb_width; rgui->last_height = fb_height; } + + if (rgui->bg_modified) + rgui->bg_modified = false; menu_display_set_framebuffer_dirty_flag(); menu_animation_ctl(MENU_ANIMATION_CTL_CLEAR_ACTIVE, NULL); @@ -511,7 +528,7 @@ static void rgui_render(void *data, bool is_idle) end = ((old_start + RGUI_TERM_HEIGHT(fb_width, fb_height)) <= (entries_end)) ? old_start + RGUI_TERM_HEIGHT(fb_width, fb_height) : entries_end; - rgui_render_background(); + rgui_render_background(rgui); menu_entries_get_title(title, sizeof(title)); @@ -657,12 +674,12 @@ static void rgui_render(void *data, bool is_idle) const char *label = menu_input_dialog_get_label_buffer(); snprintf(msg, sizeof(msg), "%s\n%s", label, str); - rgui_render_messagebox(msg); + rgui_render_messagebox(rgui, msg); } if (!string_is_empty(rgui->msgbox)) { - rgui_render_messagebox(rgui->msgbox); + rgui_render_messagebox(rgui, rgui->msgbox); free(rgui->msgbox); rgui->msgbox = NULL; rgui->force_redraw = true; @@ -693,6 +710,7 @@ static void *rgui_init(void **userdata, bool video_is_threaded) unsigned fb_width, fb_height, new_font_height; rgui_t *rgui = NULL; bool ret = false; + settings_t *settings = config_get_ptr(); menu_handle_t *menu = (menu_handle_t*)calloc(1, sizeof(*menu)); if (!menu) @@ -703,7 +721,7 @@ static void *rgui_init(void **userdata, bool video_is_threaded) if (!rgui) goto error; - *userdata = rgui; + *userdata = rgui; /* 4 extra lines to cache the checked background */ rgui_framebuf_data = (uint16_t*) @@ -730,10 +748,9 @@ static void *rgui_init(void **userdata, bool video_is_threaded) if (!ret) goto error; - if (rgui_framebuf_data) - rgui_fill_rect(rgui_framebuf_data, - fb_pitch, 0, fb_height, - fb_width, 4, rgui_gray_filler); + rgui->bg_thickness = settings->bools.menu_rgui_background_filler_thickness_enable; + rgui->border_thickness = settings->bools.menu_rgui_border_filler_thickness_enable; + rgui->bg_modified = true; rgui->last_width = fb_width; rgui->last_height = fb_height; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index dd89b20305..88ef220072 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5306,10 +5306,18 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_MENU_FRAMEBUFFER_OPACITY, PARSE_ONLY_FLOAT, false) == 0) count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_RGUI_BORDER_FILLER_ENABLE, PARSE_ONLY_BOOL, false) == 0) count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_LINEAR_FILTER, PARSE_ONLY_BOOL, false) == 0) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 22c719367f..dda74df1ce 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5194,7 +5194,39 @@ static bool setting_append_list( parent_group, general_write_handler, general_read_handler, - SD_FLAG_ADVANCED + SD_FLAG_NONE + ); + + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_rgui_background_filler_thickness_enable, + MENU_ENUM_LABEL_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, + true, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_rgui_border_filler_thickness_enable, + MENU_ENUM_LABEL_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, + true, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE ); if (setting_set) @@ -5211,7 +5243,7 @@ static bool setting_append_list( parent_group, general_write_handler, general_read_handler, - SD_FLAG_ADVANCED + SD_FLAG_NONE ); } diff --git a/msg_hash.h b/msg_hash.h index 5abcdb94d7..3b6766bce7 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -749,6 +749,8 @@ enum msg_hash_enums MENU_LABEL(MOUSE_ENABLE), MENU_LABEL(POINTER_ENABLE), MENU_LABEL(MENU_RGUI_BORDER_FILLER_ENABLE), + MENU_LABEL(MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE), + MENU_LABEL(MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE), MENU_LABEL(MENU_LINEAR_FILTER), MENU_LABEL(MENU_HORIZONTAL_ANIMATION), MENU_LABEL(NAVIGATION_WRAPAROUND), From b907ee25032713a4cc7e27197e1322ec8e28fe21 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 17:08:43 +0200 Subject: [PATCH 459/517] Add sublabels for CRT SwitchRes --- intl/msg_hash_ar.h | 4 ++++ intl/msg_hash_chs.h | 4 ++++ intl/msg_hash_cht.h | 4 ++++ intl/msg_hash_de.h | 4 ++++ intl/msg_hash_eo.h | 4 ++++ intl/msg_hash_es.h | 4 ++++ intl/msg_hash_fr.h | 4 ++++ intl/msg_hash_it.h | 4 ++++ intl/msg_hash_ja.h | 4 ++++ intl/msg_hash_ko.h | 4 ++++ intl/msg_hash_nl.h | 4 ++++ intl/msg_hash_pl.h | 4 ++++ intl/msg_hash_pt_br.h | 4 ++++ intl/msg_hash_pt_pt.h | 4 ++++ intl/msg_hash_ru.h | 4 ++++ intl/msg_hash_us.h | 11 ++++------- intl/msg_hash_vn.h | 4 ++++ menu/cbs/menu_cbs_sublabel.c | 8 ++++++++ menu/menu_setting.c | 32 +++++++++++++++++--------------- 19 files changed, 93 insertions(+), 22 deletions(-) diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index 15edc43cdb..f1891ca728 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -3441,3 +3441,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index 0261fd48a7..bd1a75d94a 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -3227,3 +3227,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index 52b50d9ba8..002f6559fc 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -3219,3 +3219,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_de.h b/intl/msg_hash_de.h index bd052b7d18..4d3ded6e72 100644 --- a/intl/msg_hash_de.h +++ b/intl/msg_hash_de.h @@ -3333,3 +3333,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 9be34208be..06bfecc29e 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -3092,3 +3092,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_es.h b/intl/msg_hash_es.h index 6132592fe1..8f02d4ab9c 100644 --- a/intl/msg_hash_es.h +++ b/intl/msg_hash_es.h @@ -5823,3 +5823,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index b5133d4f0d..ef81311026 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -3257,3 +3257,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index 7268c142e6..4243550d54 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -3315,3 +3315,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index 53db7a8bf5..eb2f81b790 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -3331,3 +3331,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_ko.h b/intl/msg_hash_ko.h index a32764fefc..9c4bd029c7 100644 --- a/intl/msg_hash_ko.h +++ b/intl/msg_hash_ko.h @@ -3218,3 +3218,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index fd2f1a6e72..756f565d06 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -3094,3 +3094,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index 8c3d081e69..cf9d0d22bb 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -3453,3 +3453,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index 69e60613c8..9ff695bb7d 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -4322,3 +4322,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_pt_pt.h b/intl/msg_hash_pt_pt.h index dc0d414f01..33bf14b9f0 100644 --- a/intl/msg_hash_pt_pt.h +++ b/intl/msg_hash_pt_pt.h @@ -3192,3 +3192,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_ru.h b/intl/msg_hash_ru.h index f3956f3c14..32e4e8269e 100644 --- a/intl/msg_hash_ru.h +++ b/intl/msg_hash_ru.h @@ -3276,3 +3276,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 87fb91209c..03699d7e83 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3476,13 +3476,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_QUALITY, "Lower this value to favor performance/lower latency over audio quality, increase if you want better audio quality at the expense of performance/lower latency.") MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_WATCH_FOR_CHANGES, "Watch shader files for changes") -MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") - -MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") - -MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") - -MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_WATCH_FOR_CHANGES, "Auto-apply changes made to shader files on disk.") MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_SHOW_DECORATIONS, @@ -3497,3 +3490,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index e8ad2f7d60..b644ce639c 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -3249,3 +3249,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index c75c117a9f..605cf8bcaa 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -48,6 +48,8 @@ return 0; \ } +default_sublabel_macro(action_bind_sublabel_crt_switchres, MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION) +default_sublabel_macro(action_bind_sublabel_crt_switchres_super, MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER) default_sublabel_macro(action_bind_sublabel_automatically_add_content_to_playlist, MENU_ENUM_SUBLABEL_AUTOMATICALLY_ADD_CONTENT_TO_PLAYLIST) default_sublabel_macro(action_bind_sublabel_driver_settings_list, MENU_ENUM_SUBLABEL_DRIVER_SETTINGS) default_sublabel_macro(action_bind_sublabel_retro_achievements_settings_list, MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS) @@ -551,6 +553,12 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, { switch (cbs->enum_idx) { + case MENU_ENUM_LABEL_CRT_SWITCH_RESOLUTION: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_crt_switchres); + break; + case MENU_ENUM_LABEL_CRT_SWITCH_RESOLUTION_SUPER: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_crt_switchres_super); + break; case MENU_ENUM_LABEL_AUDIO_RESAMPLER_QUALITY: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_resampler_quality); break; diff --git a/menu/menu_setting.c b/menu/menu_setting.c index dda74df1ce..3f7d2dde4d 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -3039,7 +3039,8 @@ static bool setting_append_list( parent_group, general_write_handler, general_read_handler, - SD_FLAG_NONE); + SD_FLAG_ADVANCED + ); CONFIG_UINT( list, list_info, @@ -3052,21 +3053,22 @@ static bool setting_append_list( parent_group, general_write_handler, general_read_handler); + settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED); - CONFIG_BOOL( - list, list_info, - &settings->bools.video_framecount_show, - MENU_ENUM_LABEL_FRAMECOUNT_SHOW, - MENU_ENUM_LABEL_VALUE_FRAMECOUNT_SHOW, - fps_show, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler, - SD_FLAG_NONE); + CONFIG_BOOL( + list, list_info, + &settings->bools.video_framecount_show, + MENU_ENUM_LABEL_FRAMECOUNT_SHOW, + MENU_ENUM_LABEL_VALUE_FRAMECOUNT_SHOW, + fps_show, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); END_SUB_GROUP(list, list_info, parent_group); START_SUB_GROUP(list, list_info, "Platform-specific", &group_info, &subgroup_info, parent_group); From dd6eb92352604b5254cdd4231ce41c9f3f321ea3 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Tue, 24 Apr 2018 10:32:31 -0500 Subject: [PATCH 460/517] Two fixes for copy_load_info.c * Additional check before copying data in clone_retro_game_info * Sizes the dest string list in string_list_clone to capacity rather than size --- runahead/copy_load_info.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/runahead/copy_load_info.c b/runahead/copy_load_info.c index 09044efda2..de9f736b39 100644 --- a/runahead/copy_load_info.c +++ b/runahead/copy_load_info.c @@ -37,12 +37,15 @@ static struct retro_game_info* clone_retro_game_info(const dest->data = NULL; dest->path = strcpy_alloc(src->path); - data = malloc(src->size); - - if (data) + if (src->size && src->data) { - memcpy(data, src->data, src->size); - dest->data = data; + data = malloc(src->size); + + if (data) + { + memcpy(data, src->data, src->size); + dest->data = data; + } } dest->size = src->size; @@ -77,9 +80,13 @@ static struct string_list *string_list_clone( dest->size = src->size; dest->cap = src->cap; + if (dest->cap < dest->size) + { + dest->cap = dest->size; + } elems = (struct string_list_elem*) - calloc(dest->size, sizeof(struct string_list_elem)); + calloc(dest->cap, sizeof(struct string_list_elem)); if (!elems) { From b2e9a328cdbe648c693f51d53f1126aef94d5952 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 17:46:49 +0200 Subject: [PATCH 461/517] Add User Interface -> Views -> Quick Menu -> Show Overlays/Remaps/Latency --- configuration.c | 3 +++ configuration.h | 3 +++ intl/msg_hash_ar.h | 12 +++++++++ intl/msg_hash_chs.h | 12 +++++++++ intl/msg_hash_cht.h | 12 +++++++++ intl/msg_hash_de.h | 12 +++++++++ intl/msg_hash_eo.h | 12 +++++++++ intl/msg_hash_es.h | 12 +++++++++ intl/msg_hash_fr.h | 12 +++++++++ intl/msg_hash_it.h | 12 +++++++++ intl/msg_hash_ja.h | 12 +++++++++ intl/msg_hash_ko.h | 12 +++++++++ intl/msg_hash_lbl.h | 6 +++++ intl/msg_hash_nl.h | 12 +++++++++ intl/msg_hash_pl.h | 12 +++++++++ intl/msg_hash_pt_br.h | 12 +++++++++ intl/msg_hash_pt_pt.h | 12 +++++++++ intl/msg_hash_ru.h | 12 +++++++++ intl/msg_hash_us.h | 12 +++++++++ intl/msg_hash_vn.h | 12 +++++++++ menu/cbs/menu_cbs_sublabel.c | 12 +++++++++ menu/menu_displaylist.c | 48 +++++++++++++++++++++++++----------- menu/menu_setting.c | 45 +++++++++++++++++++++++++++++++++ msg_hash.h | 3 +++ 24 files changed, 309 insertions(+), 15 deletions(-) diff --git a/configuration.c b/configuration.c index b258632f7d..7fac707d6c 100644 --- a/configuration.c +++ b/configuration.c @@ -1317,6 +1317,9 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("menu_show_load_content", &settings->bools.menu_show_load_content, true, menu_show_load_content, false); SETTING_BOOL("menu_show_information", &settings->bools.menu_show_information, true, menu_show_information, false); SETTING_BOOL("menu_show_configurations", &settings->bools.menu_show_configurations, true, menu_show_configurations, false); + SETTING_BOOL("menu_show_latency", &settings->bools.menu_show_latency, true, true, false); + SETTING_BOOL("menu_show_rewind", &settings->bools.menu_show_rewind, true, true, false); + SETTING_BOOL("menu_show_overlays", &settings->bools.menu_show_overlays, true, true, false); SETTING_BOOL("menu_show_help", &settings->bools.menu_show_help, true, menu_show_help, false); SETTING_BOOL("menu_show_quit_retroarch", &settings->bools.menu_show_quit_retroarch, true, menu_show_quit_retroarch, false); SETTING_BOOL("menu_show_reboot", &settings->bools.menu_show_reboot, true, menu_show_reboot, false); diff --git a/configuration.h b/configuration.h index eca18e512d..3f5fca1ded 100644 --- a/configuration.h +++ b/configuration.h @@ -147,6 +147,9 @@ typedef struct settings bool menu_show_help; bool menu_show_quit_retroarch; bool menu_show_reboot; + bool menu_show_latency; + bool menu_show_rewind; + bool menu_show_overlays; bool menu_materialui_icons_enable; bool menu_rgui_background_filler_thickness_enable; bool menu_rgui_border_filler_thickness_enable; diff --git a/intl/msg_hash_ar.h b/intl/msg_hash_ar.h index f1891ca728..0a01f9ecb3 100644 --- a/intl/msg_hash_ar.h +++ b/intl/msg_hash_ar.h @@ -3445,3 +3445,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index bd1a75d94a..792f553a67 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -3231,3 +3231,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index 002f6559fc..151e074bcc 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -3223,3 +3223,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_de.h b/intl/msg_hash_de.h index 4d3ded6e72..db9dd3ab65 100644 --- a/intl/msg_hash_de.h +++ b/intl/msg_hash_de.h @@ -3337,3 +3337,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 06bfecc29e..24bdb595b0 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -3096,3 +3096,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_es.h b/intl/msg_hash_es.h index 8f02d4ab9c..f299360f60 100644 --- a/intl/msg_hash_es.h +++ b/intl/msg_hash_es.h @@ -5827,3 +5827,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index ef81311026..5d6ad67cf8 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -3261,3 +3261,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index 4243550d54..7aa0753964 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -3319,3 +3319,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index eb2f81b790..9a67ef3a85 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -3335,3 +3335,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_ko.h b/intl/msg_hash_ko.h index 9c4bd029c7..dd9a97e9a1 100644 --- a/intl/msg_hash_ko.h +++ b/intl/msg_hash_ko.h @@ -3222,3 +3222,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index a53cd5d6e2..653c906773 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1479,3 +1479,9 @@ MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "menu_rgui_border_filler_thickness_enable") MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "menu_rgui_background_filler_thickness_enable") +MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_REWIND, + "menu_show_rewind_settings") +MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_LATENCY, + "menu_show_latency_settings") +MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_OVERLAYS, + "menu_show_overlay_settings") diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index 756f565d06..f2a087ba70 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -3098,3 +3098,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index cf9d0d22bb..5f422ca915 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -3457,3 +3457,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index 9ff695bb7d..6befea4583 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -4326,3 +4326,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_pt_pt.h b/intl/msg_hash_pt_pt.h index 33bf14b9f0..5dffae031a 100644 --- a/intl/msg_hash_pt_pt.h +++ b/intl/msg_hash_pt_pt.h @@ -3196,3 +3196,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_ru.h b/intl/msg_hash_ru.h index 32e4e8269e..a44acae10a 100644 --- a/intl/msg_hash_ru.h +++ b/intl/msg_hash_ru.h @@ -3280,3 +3280,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 03699d7e83..72fb4f19bb 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3494,3 +3494,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index b644ce639c..75c7cf8820 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -3253,3 +3253,15 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + "Show Rewind Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, + "Show/hide the Rewind options.") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY, + "Show/hide the Latency options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + "Show Latency Settings") +MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS, + "Show/hide the Overlay options.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + "Show Overlay Settings") diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 605cf8bcaa..7361b6f8a2 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -317,6 +317,9 @@ default_sublabel_macro(action_bind_sublabel_quick_menu_show_options, default_sublabel_macro(action_bind_sublabel_quick_menu_show_controls, MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_CONTROLS) default_sublabel_macro(action_bind_sublabel_quick_menu_show_cheats, MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_CHEATS) default_sublabel_macro(action_bind_sublabel_quick_menu_show_shaders, MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SHADERS) +default_sublabel_macro(action_bind_sublabel_content_show_overlays, MENU_ENUM_SUBLABEL_CONTENT_SHOW_OVERLAYS) +default_sublabel_macro(action_bind_sublabel_content_show_rewind, MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND) +default_sublabel_macro(action_bind_sublabel_content_show_latency, MENU_ENUM_SUBLABEL_CONTENT_SHOW_LATENCY) default_sublabel_macro(action_bind_sublabel_quick_menu_show_save_core_overrides, MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_CORE_OVERRIDES) default_sublabel_macro(action_bind_sublabel_quick_menu_show_save_game_overrides, MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_SAVE_GAME_OVERRIDES) default_sublabel_macro(action_bind_sublabel_quick_menu_show_information, MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_INFORMATION) @@ -757,6 +760,15 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_QUICK_MENU_SHOW_CHEATS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_quick_menu_show_cheats); break; + case MENU_ENUM_LABEL_CONTENT_SHOW_LATENCY: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_content_show_latency); + break; + case MENU_ENUM_LABEL_CONTENT_SHOW_REWIND: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_content_show_rewind); + break; + case MENU_ENUM_LABEL_CONTENT_SHOW_OVERLAYS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_content_show_overlays); + break; case MENU_ENUM_LABEL_QUICK_MENU_SHOW_SHADERS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_quick_menu_show_shaders); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 88ef220072..b162e314e6 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -2684,23 +2684,32 @@ static int menu_displaylist_parse_load_content_settings( MENU_SETTING_ACTION, 0, 0); } - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ONSCREEN_OVERLAY_SETTINGS), - msg_hash_to_str(MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS), - MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS, - MENU_SETTING_ACTION, 0, 0); + if (settings->bools.menu_show_overlays) + { + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ONSCREEN_OVERLAY_SETTINGS), + msg_hash_to_str(MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS), + MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS, + MENU_SETTING_ACTION, 0, 0); + } - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_REWIND_SETTINGS), - msg_hash_to_str(MENU_ENUM_LABEL_REWIND_SETTINGS), - MENU_ENUM_LABEL_REWIND_SETTINGS, - MENU_SETTING_ACTION, 0, 0); + if (settings->bools.menu_show_rewind) + { + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_REWIND_SETTINGS), + msg_hash_to_str(MENU_ENUM_LABEL_REWIND_SETTINGS), + MENU_ENUM_LABEL_REWIND_SETTINGS, + MENU_SETTING_ACTION, 0, 0); + } - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_LATENCY_SETTINGS), - msg_hash_to_str(MENU_ENUM_LABEL_LATENCY_SETTINGS), - MENU_ENUM_LABEL_LATENCY_SETTINGS, - MENU_SETTING_ACTION, 0, 0); + if (settings->bools.menu_show_latency) + { + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_LATENCY_SETTINGS), + msg_hash_to_str(MENU_ENUM_LABEL_LATENCY_SETTINGS), + MENU_ENUM_LABEL_LATENCY_SETTINGS, + MENU_SETTING_ACTION, 0, 0); + } #if 0 menu_entries_append_enum(info->list, @@ -5272,6 +5281,15 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_QUICK_MENU_SHOW_SHADERS, PARSE_ONLY_BOOL, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CONTENT_SHOW_REWIND, + PARSE_ONLY_BOOL, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CONTENT_SHOW_LATENCY, + PARSE_ONLY_BOOL, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CONTENT_SHOW_OVERLAYS, + PARSE_ONLY_BOOL, false); menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_QUICK_MENU_SHOW_SAVE_CORE_OVERRIDES, PARSE_ONLY_BOOL, false); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 3f7d2dde4d..5717d2f7d1 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5668,6 +5668,51 @@ static bool setting_append_list( general_read_handler, SD_FLAG_LAKKA_ADVANCED); + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_show_overlays, + MENU_ENUM_LABEL_CONTENT_SHOW_OVERLAYS, + MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_OVERLAYS, + true, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_LAKKA_ADVANCED); + + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_show_latency, + MENU_ENUM_LABEL_CONTENT_SHOW_LATENCY, + MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_LATENCY, + true, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_LAKKA_ADVANCED); + + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_show_rewind, + MENU_ENUM_LABEL_CONTENT_SHOW_REWIND, + MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, + true, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_LAKKA_ADVANCED); + CONFIG_BOOL( list, list_info, &settings->bools.menu_show_help, diff --git a/msg_hash.h b/msg_hash.h index 3b6766bce7..3b3f9842d5 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -769,6 +769,9 @@ enum msg_hash_enums MENU_LABEL(XMB_MAIN_MENU_ENABLE_SETTINGS), MENU_LABEL(XMB_MENU_COLOR_THEME), MENU_LABEL(XMB_SHADOWS_ENABLE), + MENU_LABEL(CONTENT_SHOW_REWIND), + MENU_LABEL(CONTENT_SHOW_LATENCY), + MENU_LABEL(CONTENT_SHOW_OVERLAYS), MENU_LABEL(CONTENT_SHOW_SETTINGS), MENU_LABEL(CONTENT_SHOW_SETTINGS_PASSWORD), MENU_LABEL(CONTENT_SHOW_FAVORITES), From 31415f760a668acbdd40cb45fd0b4b60c12ca4c3 Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Tue, 24 Apr 2018 17:47:02 +0200 Subject: [PATCH 462/517] XMB PSP layout thumbnail scaling. --- menu/drivers/xmb.c | 89 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 10 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 937b1fd2e2..41f8ba3155 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -183,6 +183,7 @@ enum typedef struct xmb_handle { bool mouse_show; + bool use_ps3_layout; uint8_t system_tab_end; uint8_t tabs[XMB_SYSTEM_TAB_MAX_LENGTH]; @@ -2506,6 +2507,7 @@ static int xmb_draw_item( if (string_is_empty(entry->value)) { if (xmb->savestate_thumbnail || + !xmb->use_ps3_layout || (!string_is_equal (thumb_ident, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) @@ -3059,8 +3061,9 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } /* Right thumbnail big size */ - if (!settings->bools.menu_xmb_vertical_thumbnails || - (settings->bools.menu_xmb_vertical_thumbnails && !xmb->left_thumbnail)) + if (xmb->use_ps3_layout && + (!settings->bools.menu_xmb_vertical_thumbnails || + (settings->bools.menu_xmb_vertical_thumbnails && !xmb->left_thumbnail))) { /* Do not draw the right thumbnail if there is no space available */ @@ -3129,9 +3132,10 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Left thumbnail in the left margin */ /* Do not draw the left thumbnail if there is no space available */ - if (!settings->bools.menu_xmb_vertical_thumbnails && + if (xmb->use_ps3_layout && + !settings->bools.menu_xmb_vertical_thumbnails && (xmb->margins_screen_top + xmb->icon_size * - (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) + (!(xmb->depth == 1)? 2.1 : 1) + min_thumb_size) <= (float)height) { /* Left Thumbnail in the left margin */ @@ -3196,7 +3200,8 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } /* No Right Thumbnail, draw only the left one big size */ - if (settings->bools.menu_xmb_vertical_thumbnails && !xmb->thumbnail) + if (xmb->use_ps3_layout && + settings->bools.menu_xmb_vertical_thumbnails && !xmb->thumbnail) { /* Do not draw the left thumbnail if there is no space available */ @@ -3263,6 +3268,68 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) } } + /* PSP Layout Only - Left thumbnail in the left margin */ + /* Do not draw the left thumbnail if there is no space available */ + if (!xmb->use_ps3_layout && + (xmb->margins_screen_top + xmb->icon_size * 1.5) + <= (float)height) + { + /* Left Thumbnail in the left margin */ + + if (xmb->left_thumbnail + && !string_is_equal(xmb_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + /* Limit left thumbnail width */ + + float left_thumb_width = 0.0f; + float left_thumb_height = 0.0f; + float thumb_max_width = xmb->icon_size * 2.4; + +#ifdef XMB_DEBUG + RARCH_LOG("[XMB left thumbnail] width: %.2f, height: %.2f\n", + xmb->left_thumbnail_width, xmb->left_thumbnail_height); + RARCH_LOG("[XMB left thumbnail] w: %.2f, h: %.2f\n", width, height); +#endif + + if (xmb->left_thumbnail_width > thumb_max_width) + { + left_thumb_width = xmb->left_thumbnail_width * + (thumb_max_width / xmb->left_thumbnail_width); + left_thumb_height = xmb->left_thumbnail_height * + (thumb_max_width / xmb->left_thumbnail_width); + } + else + { + left_thumb_width = xmb->left_thumbnail_width; + left_thumb_height = xmb->left_thumbnail_height; + } + + /* Limit left thumbnail height to screen height + margin. */ + if (xmb->margins_screen_top + xmb->icon_size + + left_thumb_height >= ((float)height - (xmb->icon_size * 0.5))) + { + left_thumb_width = left_thumb_width * + (((float)height - (xmb->icon_size * 0.5) - + xmb->margins_screen_top - xmb->icon_size) / + left_thumb_height); + + left_thumb_height = left_thumb_height * + (((float)height - (xmb->icon_size * 0.5) - + xmb->margins_screen_top - xmb->icon_size) / + left_thumb_height); + } + + xmb_draw_thumbnail(video_info, + xmb, &coord_white[0], width, height, + ((thumb_max_width - left_thumb_width) / 2), + xmb->margins_screen_top + (xmb->icon_size * (!(xmb->depth == 1)? 2.2 : 0.75)) + + left_thumb_height, + left_thumb_width, left_thumb_height, + xmb->left_thumbnail); + } + } + /* Clock image */ menu_display_set_alpha(coord_white, MIN(xmb->alpha, 1.00f)); @@ -3442,7 +3509,9 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Right side 2 thumbnails on top of each other */ /* here to be displayed above the horizontal icons */ - if (xmb->left_thumbnail && xmb->thumbnail && settings->bools.menu_xmb_vertical_thumbnails) + if (xmb->use_ps3_layout && + xmb->left_thumbnail && xmb->thumbnail && + settings->bools.menu_xmb_vertical_thumbnails) { /* Do not draw the right thumbnail if there is no space available */ if (((xmb->margins_screen_top + @@ -3782,11 +3851,11 @@ static void xmb_layout(xmb_handle_t *xmb) /* Automatic */ case 0: { - bool use_ps3_layout = false; - use_ps3_layout = width > 320 && height > 240; + xmb->use_ps3_layout = false; + xmb->use_ps3_layout = width > 320 && height > 240; /* Mimic the layout of the PSP instead of the PS3 on tiny screens */ - if (use_ps3_layout) + if (xmb->use_ps3_layout) xmb_layout_ps3(xmb, width); else xmb_layout_psp(xmb, width); @@ -3927,7 +3996,7 @@ static void *xmb_init(void **userdata, bool video_is_threaded) /* xmb_scale 50 = {2.5, 2.5, 2, 1.7, 2.5, 4, 2.4, 2.5} */ /* xmb_scale 75 = { 2, 1.6, 1.6, 1.4, 1.5, 2.3, 1.9, 1.3} */ - if (scale_value < 100) + if (scale_value < 100 && xmb->use_ps3_layout) { /* text length & word wrap (base 35 apply to file browser, 1st column) */ scale_mod[0] = -0.03 * scale_value + 4.083; From d851cbf3d58a05b6b4b03711371c0cf2d6f1bbe6 Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Tue, 24 Apr 2018 18:14:18 +0200 Subject: [PATCH 463/517] Fix console layout case. --- menu/drivers/xmb.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 41f8ba3155..c4a163daf1 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3863,11 +3863,17 @@ static void xmb_layout(xmb_handle_t *xmb) break; /* PS3 */ case 1: - xmb_layout_ps3(xmb, width); + { + xmb->use_ps3_layout = true; + xmb_layout_ps3(xmb, width); + } break; /* PSP */ case 2: - xmb_layout_psp(xmb, width); + { + xmb->use_ps3_layout = false; + xmb_layout_psp(xmb, width); + } break; } From c92142cdbde368971ee490e08337b250ef184b9c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 18:33:19 +0200 Subject: [PATCH 464/517] Duplicate latency settings --- menu/menu_displaylist.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index b162e314e6..7553f8082d 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -6001,6 +6001,22 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_SWAP_INTERVAL, PARSE_ONLY_UINT, false); + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_HARD_SYNC, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_HARD_SYNC_FRAMES, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_FRAME_DELAY, + PARSE_ONLY_UINT, false) == 0) + count++; menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VIDEO_BLACK_FRAME_INSERTION, PARSE_ONLY_BOOL, false); @@ -6061,6 +6077,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_AUDIO_SYNC, PARSE_ONLY_BOOL, false); + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_AUDIO_LATENCY, + PARSE_ONLY_UINT, false) == 0) + count++; menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_AUDIO_RESAMPLER_QUALITY, PARSE_ONLY_UINT, false); @@ -6106,6 +6126,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_UNIFIED_MENU_CONTROLS, PARSE_ONLY_BOOL, false); + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_INPUT_POLL_TYPE_BEHAVIOR, + PARSE_ONLY_UINT, false) == 0) + count++; ret = menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_INPUT_ICADE_ENABLE, PARSE_ONLY_BOOL, false); From ea69340130f04d292657b589449bec0345b52d4b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 24 Apr 2018 18:36:35 +0200 Subject: [PATCH 465/517] Update --- CHANGES.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5f6eae415a..7ec25200e4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,13 +15,14 @@ with D3D11 driver except for there being no hardware rendering right now. - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. First core to use this is PPSSPP. - D3D11: Increase backwards compatibility, shaders compile with Shader Model 4.0 now, added support for more feature levels. -- D3D11/D3D12: Fix crashes with completely black or white thumbnail textures in XMB. +- D3D10/D3D11/D3D12: Fix crashes with completely black or white thumbnail textures in XMB. - LIBRETRO: Addition - Functions to enable and disable audio and video, and an environment function to query status of audio and video enables. - LOCALIZATION: Update Italian translation. - LOCALIZATION: Update Polish translation. -- MENU: Add Rewind/Latency/Overlay settings to Quick Menu +- MENU: Add Rewind/Latency/Overlay settings to Quick Menu, add options to show/hide them (User Interface -> Views -> Quick Menu) - MENU/RGUI: Only show Menu Linear Filter for RGUI and only show it for video drivers that implement it (D3D8/9/10/11/12/GL) +- MENU/RGUI: Add User Interface -> Appearance options. - MENU/RGUI: D3D8/D3D9: Hookup Menu Linear Filter - MENU/XMB: Disable XMB shadow icons by default for PowerPC and ARM for performance reasons. - MENU/XMB: Left/right thumbnails are now automatically scaled according to layout. From d65b387d0fc35797127c4ac0fb0b70f98a7e4d7e Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Tue, 24 Apr 2018 19:03:57 +0200 Subject: [PATCH 466/517] Update AndroidManifest.xml --- pkg/android/phoenix/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/android/phoenix/AndroidManifest.xml b/pkg/android/phoenix/AndroidManifest.xml index 64fb606419..cefb6e34a1 100644 --- a/pkg/android/phoenix/AndroidManifest.xml +++ b/pkg/android/phoenix/AndroidManifest.xml @@ -1,7 +1,7 @@ From 2160f185f1b2e24653f04a0a77e4dca5be26d025 Mon Sep 17 00:00:00 2001 From: bparker06 Date: Tue, 24 Apr 2018 19:34:31 -0400 Subject: [PATCH 467/517] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 7ec25200e4..45f39f49df 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ with D3D11 driver except for there being no hardware rendering right now. - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. First core to use this is PPSSPP. - D3D11: Increase backwards compatibility, shaders compile with Shader Model 4.0 now, added support for more feature levels. - D3D10/D3D11/D3D12: Fix crashes with completely black or white thumbnail textures in XMB. +- GUI: Support disabling window decorations on Windows and Linux. - LIBRETRO: Addition - Functions to enable and disable audio and video, and an environment function to query status of audio and video enables. - LOCALIZATION: Update Italian translation. - LOCALIZATION: Update Polish translation. From ca4f70db221d4e3968e4f695fd5c9ee79da8e6f0 Mon Sep 17 00:00:00 2001 From: Celerizer Date: Tue, 24 Apr 2018 21:31:07 -0500 Subject: [PATCH 468/517] [Cheevos] Store only login token, not password --- cheevos/cheevos.c | 103 +++++++++++++++++++++++++++++++++++----------- configuration.c | 2 + configuration.h | 1 + 3 files changed, 81 insertions(+), 25 deletions(-) diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 35d4857b55..046bf53620 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -107,6 +107,8 @@ #define CHEEVOS_JSON_KEY_LEADERBOARDS 0xf1247d2dU #define CHEEVOS_JSON_KEY_MEM 0x0b8807e4U #define CHEEVOS_JSON_KEY_FORMAT 0xb341208eU +#define CHEEVOS_JSON_KEY_SUCCESS 0x110461deU +#define CHEEVOS_JSON_KEY_ERROR 0x0d2011cfU typedef struct { @@ -3587,29 +3589,46 @@ found: { char urle_user[64]; - char urle_pwd[64]; + char urle_login[64]; const char *username = coro ? coro->settings->arrays.cheevos_username : NULL; - const char *password = coro ? coro->settings->arrays.cheevos_password : NULL; + const char *login; + bool via_token; - if (!username || !*username || !password || !*password) + if (coro) + { + if (string_is_empty(coro->settings->arrays.cheevos_password)) + { + via_token = true; + login = coro->settings->arrays.cheevos_token; + } + else + { + via_token = false; + login = coro->settings->arrays.cheevos_password; + } + } + else + login = NULL; + + if (string_is_empty(username) || string_is_empty(login)) { runloop_msg_queue_push( - "Missing Retro Achievements account information.", + "Missing RetroAchievements account information.", 0, 5 * 60, false); runloop_msg_queue_push( "Please fill in your account information in Settings.", 0, 5 * 60, false); - RARCH_ERR("[CHEEVOS]: username and/or password not informed.\n"); + RARCH_ERR("[CHEEVOS]: login info not informed.\n"); CORO_STOP(); } cheevos_url_encode(username, urle_user, sizeof(urle_user)); - cheevos_url_encode(password, urle_pwd, sizeof(urle_pwd)); + cheevos_url_encode(login, urle_login, sizeof(urle_login)); snprintf( coro->url, sizeof(coro->url), - "http://retroachievements.org/dorequest.php?r=login&u=%s&p=%s", - urle_user, urle_pwd + "http://retroachievements.org/dorequest.php?r=login&u=%s&%c=%s", + urle_user, via_token ? 't' : 'p', urle_login ); coro->url[sizeof(coro->url) - 1] = 0; @@ -3624,32 +3643,66 @@ found: if (coro->json) { - int res = cheevos_get_value( + char error_response[64]; + cheevos_get_value( + coro->json, + CHEEVOS_JSON_KEY_ERROR, + error_response, + sizeof(error_response) + ); + + /* No error, continue with login */ + if (string_is_empty(error_response)) + { + int res = cheevos_get_value( coro->json, CHEEVOS_JSON_KEY_TOKEN, cheevos_locals.token, sizeof(cheevos_locals.token)); + if ((void*)coro->json) + free((void*)coro->json); + + if (!res) + { + if (coro->settings->bools.cheevos_verbose_enable) + { + char msg[256]; + snprintf(msg, sizeof(msg), + "RetroAchievements: Logged in as \"%s\".", + coro->settings->arrays.cheevos_username); + msg[sizeof(msg) - 1] = 0; + runloop_msg_queue_push(msg, 0, 3 * 60, false); + } + + /* Save token to config and clear pass on success */ + *coro->settings->arrays.cheevos_password = '\0'; + strncpy( + coro->settings->arrays.cheevos_token, + cheevos_locals.token, sizeof(cheevos_locals.token) + ); + CORO_RET(); + } + } + if ((void*)coro->json) free((void*)coro->json); - - if (!res) - { - if (coro->settings->bools.cheevos_verbose_enable) - { - char msg[256]; - snprintf(msg, sizeof(msg), - "RetroAchievements: logged in as \"%s\".", - coro->settings->arrays.cheevos_username); - msg[sizeof(msg) - 1] = 0; - runloop_msg_queue_push(msg, 0, 3 * 60, false); - } - CORO_RET(); - } + + /* Site returned error, display it */ + char error_message[256]; + snprintf(error_message, sizeof(error_message), + "RetroAchievements: %s", + error_response); + error_message[sizeof(error_message) - 1] = 0; + runloop_msg_queue_push(error_message, 0, 5 * 60, false); + *coro->settings->arrays.cheevos_token = '\0'; + + CORO_STOP(); } - - runloop_msg_queue_push("Retro Achievements login error.", 0, 5 * 60, false); + + runloop_msg_queue_push("RetroAchievements: Error contacting server.", 0, 5 * 60, false); RARCH_ERR("[CHEEVOS]: error getting user token.\n"); + CORO_STOP(); /************************************************************************** diff --git a/configuration.c b/configuration.c index 7fac707d6c..5a8a78b8aa 100644 --- a/configuration.c +++ b/configuration.c @@ -1033,6 +1033,7 @@ static struct config_array_setting *populate_settings_array(settings_t *settings #ifdef HAVE_CHEEVOS SETTING_ARRAY("cheevos_username", settings->arrays.cheevos_username, false, NULL, true); SETTING_ARRAY("cheevos_password", settings->arrays.cheevos_password, false, NULL, true); + SETTING_ARRAY("cheevos_token", settings->arrays.cheevos_token, false, NULL, true); #endif SETTING_ARRAY("video_context_driver", settings->arrays.video_context_driver, false, NULL, true); SETTING_ARRAY("audio_driver", settings->arrays.audio_driver, false, NULL, true); @@ -1724,6 +1725,7 @@ static void config_set_defaults(void) #ifdef HAVE_CHEEVOS *settings->arrays.cheevos_username = '\0'; *settings->arrays.cheevos_password = '\0'; + *settings->arrays.cheevos_token = '\0'; #endif input_config_reset(); diff --git a/configuration.h b/configuration.h index 3f5fca1ded..89a53b6b42 100644 --- a/configuration.h +++ b/configuration.h @@ -411,6 +411,7 @@ typedef struct settings char menu_driver[32]; char cheevos_username[32]; char cheevos_password[32]; + char cheevos_token[32]; char video_context_driver[32]; char audio_driver[32]; char audio_resampler[32]; From f33fa3d566105b157b272e0369a7ff82a000598c Mon Sep 17 00:00:00 2001 From: gblues Date: Tue, 24 Apr 2018 21:20:08 -0700 Subject: [PATCH 469/517] Fix pad leak in kpad (wiimote) driver == DETAILS This is the wiimote version of the same bug I previously fixed in the HID driver, where disconnected pads didn't actually invoke the unregister task. This has an extra wrinkle, in that we *also* need to invoke the unregister task when the wiimote device changes (e.g. user plugs in a nunchuk or classic controller). Now, there's still the problem of the "disconnect" detection being broken; so a consequence of this commit is OSD spam. However, the actual wiimote input is processed successfully and there's no noticeable issues in the pad handling. == TESTING Using Mario 3, I played a level in which I started as bare wiimote, then hot-plugged the nunchuk, and the input switched automatically. At the end of the level, I hot-unplugged the nunchuk and it automatically reverted to horizontal layout; and the pad remained 100% responsive the entire time. --- wiiu/input/kpad_driver.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/wiiu/input/kpad_driver.c b/wiiu/input/kpad_driver.c index 8453e597f4..c958e43bb4 100644 --- a/wiiu/input/kpad_driver.c +++ b/wiiu/input/kpad_driver.c @@ -30,6 +30,7 @@ static void kpad_get_buttons(unsigned pad, input_bits_t *state); static int16_t kpad_axis(unsigned pad, uint32_t axis); static void kpad_poll(void); static const char *kpad_name(unsigned pad); +static void kpad_deregister(unsigned channel); typedef struct _wiimote_state wiimote_state; @@ -136,7 +137,11 @@ static void kpad_register(unsigned channel, uint8_t device_type) { if (wiimotes[channel].type != device_type) { - int slot = get_slot_for_channel(channel); + int slot; + + kpad_deregister(channel); + slot = get_slot_for_channel(channel); + if(slot < 0) { RARCH_ERR("Couldn't get a slot for this remote.\n"); @@ -188,6 +193,19 @@ static void kpad_poll_one_channel(unsigned channel, KPADData *kpad) } } +static void kpad_deregister(unsigned channel) +{ + int slot = channel_slot_map[channel]; + + if(slot >= 0) + { + input_autoconfigure_disconnect(slot, kpad_driver.name(slot)); + wiimotes[channel].type = WIIMOTE_TYPE_NONE; + hid_instance.pad_list[slot].connected = false; + channel_slot_map[channel] = -1; + } +} + static void kpad_poll(void) { unsigned channel; @@ -200,13 +218,7 @@ static void kpad_poll(void) result = KPADRead(channel, &kpad, 1); if (result == 0) { - int slot = channel_slot_map[channel]; - - if(slot > 0) - { - hid_instance.pad_list[slot].connected = false; - channel_slot_map[channel] = -1; - } + kpad_deregister(channel); continue; } From 07864aebb4e2873c10678835f06574cd70d9f815 Mon Sep 17 00:00:00 2001 From: gblues Date: Tue, 24 Apr 2018 21:46:42 -0700 Subject: [PATCH 470/517] Add fault-tolerance to kpad driver == DETAILS So, the KPadRead function will sometimes return 0, but this doesn't mean the wiimote is actually disconnected. It's usually something transient, like the BT chip has nothing to send or whatever. I don't know. So, I added a buffer so that it won't disconnect the pad without 5 consecutive 0-reads. This is a temporary hack; a proper solution will use the Wii U's callback mechanisms to do wiimote detection. But that's a separate project. This at least prevents OSD spam. == TESTING Tested locally. Verified that connecting/disconnecting nunchuk during play still works properly. --- wiiu/input/kpad_driver.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/wiiu/input/kpad_driver.c b/wiiu/input/kpad_driver.c index c958e43bb4..a3c7078357 100644 --- a/wiiu/input/kpad_driver.c +++ b/wiiu/input/kpad_driver.c @@ -206,6 +206,8 @@ static void kpad_deregister(unsigned channel) } } +static int poll_failures[WIIU_WIIMOTE_CHANNELS] = { 0, 0, 0, 0 }; + static void kpad_poll(void) { unsigned channel; @@ -217,10 +219,16 @@ static void kpad_poll(void) memset(&kpad, 0, sizeof(kpad)); result = KPADRead(channel, &kpad, 1); + /* this is a hack to prevent spurious disconnects */ + /* TODO: use KPADSetConnectCallback and use callbacks to detect */ + /* pad disconnects properly. */ if (result == 0) { - kpad_deregister(channel); + poll_failures[channel]++; + if(poll_failures[channel] > 5) + kpad_deregister(channel); continue; } + poll_failures[channel] = 0; kpad_poll_one_channel(channel, &kpad); } From de111ea9030cf7cb421eb6236a447efe0e51ac59 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 25 Apr 2018 10:10:22 +0200 Subject: [PATCH 471/517] Should fix some zip archives potentially not working on Linux; the stream member of zlib was being set to random memory, so it was attempted to be freed on line 73 of trans_stream_zlib.c --- libretro-common/file/archive_file_zlib.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libretro-common/file/archive_file_zlib.c b/libretro-common/file/archive_file_zlib.c index 3870dacaed..07fec55fd4 100644 --- a/libretro-common/file/archive_file_zlib.c +++ b/libretro-common/file/archive_file_zlib.c @@ -240,22 +240,22 @@ static int zip_file_read( const char *needle, void **buf, const char *optional_outfile) { - file_archive_transfer_t zlib; + file_archive_transfer_t zlib = {0}; struct archive_extract_userdata userdata = {{0}}; - bool returnerr = true; - int ret = 0; + bool returnerr = true; + int ret = 0; - zlib.type = ARCHIVE_TRANSFER_INIT; + zlib.type = ARCHIVE_TRANSFER_INIT; - userdata.decomp_state.needle = NULL; - userdata.decomp_state.opt_file = NULL; - userdata.decomp_state.found = false; - userdata.decomp_state.buf = buf; + userdata.decomp_state.needle = NULL; + userdata.decomp_state.opt_file = NULL; + userdata.decomp_state.found = false; + userdata.decomp_state.buf = buf; if (needle) - userdata.decomp_state.needle = strdup(needle); + userdata.decomp_state.needle = strdup(needle); if (optional_outfile) - userdata.decomp_state.opt_file = strdup(optional_outfile); + userdata.decomp_state.opt_file = strdup(optional_outfile); do { From 634416290035bc64dfb9041ea118bda994efaa47 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Wed, 25 Apr 2018 10:13:57 +0200 Subject: [PATCH 472/517] Update CHANGES.md --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 45f39f49df..7e1d62cc86 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ - COMMON: Fix invalid long command line options causing infinite loop on Windows - COMMON: Add OSD statistics for video/audio/core. - COMMON: Added runahead system; allows you to drive down latency even further. +- COMMON: Fix buggy behavior that could happen with ZIP file reading on some platforms as a result of not initializing struct. - CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. - CHEEVOS: Fixed incompatibilities with Neo Geo Pocket achievement sets. @@ -57,7 +58,7 @@ that support it (so far this includes - D3D8/D3D9, OpenGL, Vulkan) - WINDOWS/MSVC 2003/2005/2010/2013/2015/2017: Add Cheevos support. - VITA: Bugfix for 'PS Vita takes many time to start to accept input' issue. - X11: Allow compositor disabling on X11 fullscreen through _NET_WM_BYPASS_COMPOSITOR -- X11: Prioritize _NET_WM_STATE_FULLSCREEN in true fullscreen mode +- X11: Prioritize _NET_WM_STATE_FULLSCREEN_ in true fullscreen mode - WIIU: Fix OOB read/write in keyboard driver. # 1.7.1 From 7902a606698e61cb2e56909c45845d90dc224f5e Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Wed, 25 Apr 2018 10:15:40 +0200 Subject: [PATCH 473/517] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 7e1d62cc86..6a84b56d14 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ - CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. - CHEEVOS: Fixed incompatibilities with Neo Geo Pocket achievement sets. +- CHEEVOS: Store only login token, not password. - D3D10: Added D3D10 driver to release build. Has working shaders (Slang), overlay, and menu display driver support. Should be on par capabilities wise with D3D11 driver except for there being no hardware rendering right now. - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. First core to use this is PPSSPP. From 2178f7b8ce7a0fde0290397ae5409f77dbb49a02 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 25 Apr 2018 10:21:26 +0200 Subject: [PATCH 474/517] Update libretro-common --- libretro-common/audio/audio_mixer.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libretro-common/audio/audio_mixer.c b/libretro-common/audio/audio_mixer.c index 4dffa95c84..7988ff6517 100644 --- a/libretro-common/audio/audio_mixer.c +++ b/libretro-common/audio/audio_mixer.c @@ -44,7 +44,7 @@ #define STB_VORBIS_NO_STDIO #define STB_VORBIS_NO_CRT -#include +#include #endif #ifdef HAVE_IBXM @@ -412,7 +412,7 @@ static bool audio_mixer_play_ogg( { stb_vorbis_info info; int res = 0; - float ratio = 0.0f; + float ratio = 1.0f; unsigned samples = 0; void *ogg_buffer = NULL; void *resampler_data = NULL; @@ -665,8 +665,8 @@ static void audio_mixer_mix_ogg(float* buffer, size_t num_frames, float volume) { int i; - struct resampler_data info; - float temp_buffer[AUDIO_MIXER_TEMP_OGG_BUFFER]; + struct resampler_data info = { 0 }; + float temp_buffer[AUDIO_MIXER_TEMP_OGG_BUFFER] = { 0 }; unsigned buf_free = (unsigned)(num_frames * 2); unsigned temp_samples = 0; float* pcm = NULL; @@ -704,7 +704,10 @@ again: info.output_frames = 0; info.ratio = voice->types.ogg.ratio; - voice->types.ogg.resampler->process(voice->types.ogg.resampler_data, &info); + if (voice->types.ogg.resampler) + voice->types.ogg.resampler->process(voice->types.ogg.resampler_data, &info); + else + memcpy(voice->types.ogg.buffer, temp_buffer, temp_samples * sizeof(float)); voice->types.ogg.position = 0; voice->types.ogg.samples = voice->types.ogg.buf_samples; } From 212d7bfbe1084ac7a1bc62be3833750a3971f1cc Mon Sep 17 00:00:00 2001 From: Celerizer Date: Tue, 24 Apr 2018 21:31:07 -0500 Subject: [PATCH 475/517] [Cheevos] Store only login token, not password --- cheevos/cheevos.c | 103 +++++++++++++++++++++++++++++++++++----------- configuration.c | 2 + configuration.h | 1 + 3 files changed, 81 insertions(+), 25 deletions(-) diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 35d4857b55..046bf53620 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -107,6 +107,8 @@ #define CHEEVOS_JSON_KEY_LEADERBOARDS 0xf1247d2dU #define CHEEVOS_JSON_KEY_MEM 0x0b8807e4U #define CHEEVOS_JSON_KEY_FORMAT 0xb341208eU +#define CHEEVOS_JSON_KEY_SUCCESS 0x110461deU +#define CHEEVOS_JSON_KEY_ERROR 0x0d2011cfU typedef struct { @@ -3587,29 +3589,46 @@ found: { char urle_user[64]; - char urle_pwd[64]; + char urle_login[64]; const char *username = coro ? coro->settings->arrays.cheevos_username : NULL; - const char *password = coro ? coro->settings->arrays.cheevos_password : NULL; + const char *login; + bool via_token; - if (!username || !*username || !password || !*password) + if (coro) + { + if (string_is_empty(coro->settings->arrays.cheevos_password)) + { + via_token = true; + login = coro->settings->arrays.cheevos_token; + } + else + { + via_token = false; + login = coro->settings->arrays.cheevos_password; + } + } + else + login = NULL; + + if (string_is_empty(username) || string_is_empty(login)) { runloop_msg_queue_push( - "Missing Retro Achievements account information.", + "Missing RetroAchievements account information.", 0, 5 * 60, false); runloop_msg_queue_push( "Please fill in your account information in Settings.", 0, 5 * 60, false); - RARCH_ERR("[CHEEVOS]: username and/or password not informed.\n"); + RARCH_ERR("[CHEEVOS]: login info not informed.\n"); CORO_STOP(); } cheevos_url_encode(username, urle_user, sizeof(urle_user)); - cheevos_url_encode(password, urle_pwd, sizeof(urle_pwd)); + cheevos_url_encode(login, urle_login, sizeof(urle_login)); snprintf( coro->url, sizeof(coro->url), - "http://retroachievements.org/dorequest.php?r=login&u=%s&p=%s", - urle_user, urle_pwd + "http://retroachievements.org/dorequest.php?r=login&u=%s&%c=%s", + urle_user, via_token ? 't' : 'p', urle_login ); coro->url[sizeof(coro->url) - 1] = 0; @@ -3624,32 +3643,66 @@ found: if (coro->json) { - int res = cheevos_get_value( + char error_response[64]; + cheevos_get_value( + coro->json, + CHEEVOS_JSON_KEY_ERROR, + error_response, + sizeof(error_response) + ); + + /* No error, continue with login */ + if (string_is_empty(error_response)) + { + int res = cheevos_get_value( coro->json, CHEEVOS_JSON_KEY_TOKEN, cheevos_locals.token, sizeof(cheevos_locals.token)); + if ((void*)coro->json) + free((void*)coro->json); + + if (!res) + { + if (coro->settings->bools.cheevos_verbose_enable) + { + char msg[256]; + snprintf(msg, sizeof(msg), + "RetroAchievements: Logged in as \"%s\".", + coro->settings->arrays.cheevos_username); + msg[sizeof(msg) - 1] = 0; + runloop_msg_queue_push(msg, 0, 3 * 60, false); + } + + /* Save token to config and clear pass on success */ + *coro->settings->arrays.cheevos_password = '\0'; + strncpy( + coro->settings->arrays.cheevos_token, + cheevos_locals.token, sizeof(cheevos_locals.token) + ); + CORO_RET(); + } + } + if ((void*)coro->json) free((void*)coro->json); - - if (!res) - { - if (coro->settings->bools.cheevos_verbose_enable) - { - char msg[256]; - snprintf(msg, sizeof(msg), - "RetroAchievements: logged in as \"%s\".", - coro->settings->arrays.cheevos_username); - msg[sizeof(msg) - 1] = 0; - runloop_msg_queue_push(msg, 0, 3 * 60, false); - } - CORO_RET(); - } + + /* Site returned error, display it */ + char error_message[256]; + snprintf(error_message, sizeof(error_message), + "RetroAchievements: %s", + error_response); + error_message[sizeof(error_message) - 1] = 0; + runloop_msg_queue_push(error_message, 0, 5 * 60, false); + *coro->settings->arrays.cheevos_token = '\0'; + + CORO_STOP(); } - - runloop_msg_queue_push("Retro Achievements login error.", 0, 5 * 60, false); + + runloop_msg_queue_push("RetroAchievements: Error contacting server.", 0, 5 * 60, false); RARCH_ERR("[CHEEVOS]: error getting user token.\n"); + CORO_STOP(); /************************************************************************** diff --git a/configuration.c b/configuration.c index 7fac707d6c..5a8a78b8aa 100644 --- a/configuration.c +++ b/configuration.c @@ -1033,6 +1033,7 @@ static struct config_array_setting *populate_settings_array(settings_t *settings #ifdef HAVE_CHEEVOS SETTING_ARRAY("cheevos_username", settings->arrays.cheevos_username, false, NULL, true); SETTING_ARRAY("cheevos_password", settings->arrays.cheevos_password, false, NULL, true); + SETTING_ARRAY("cheevos_token", settings->arrays.cheevos_token, false, NULL, true); #endif SETTING_ARRAY("video_context_driver", settings->arrays.video_context_driver, false, NULL, true); SETTING_ARRAY("audio_driver", settings->arrays.audio_driver, false, NULL, true); @@ -1724,6 +1725,7 @@ static void config_set_defaults(void) #ifdef HAVE_CHEEVOS *settings->arrays.cheevos_username = '\0'; *settings->arrays.cheevos_password = '\0'; + *settings->arrays.cheevos_token = '\0'; #endif input_config_reset(); diff --git a/configuration.h b/configuration.h index 3f5fca1ded..89a53b6b42 100644 --- a/configuration.h +++ b/configuration.h @@ -411,6 +411,7 @@ typedef struct settings char menu_driver[32]; char cheevos_username[32]; char cheevos_password[32]; + char cheevos_token[32]; char video_context_driver[32]; char audio_driver[32]; char audio_resampler[32]; From 8e1c2e48c45de12249112a2d9bc8f67c39abdd89 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Wed, 25 Apr 2018 10:13:57 +0200 Subject: [PATCH 476/517] Update CHANGES.md --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 45f39f49df..7e1d62cc86 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ - COMMON: Fix invalid long command line options causing infinite loop on Windows - COMMON: Add OSD statistics for video/audio/core. - COMMON: Added runahead system; allows you to drive down latency even further. +- COMMON: Fix buggy behavior that could happen with ZIP file reading on some platforms as a result of not initializing struct. - CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. - CHEEVOS: Fixed incompatibilities with Neo Geo Pocket achievement sets. @@ -57,7 +58,7 @@ that support it (so far this includes - D3D8/D3D9, OpenGL, Vulkan) - WINDOWS/MSVC 2003/2005/2010/2013/2015/2017: Add Cheevos support. - VITA: Bugfix for 'PS Vita takes many time to start to accept input' issue. - X11: Allow compositor disabling on X11 fullscreen through _NET_WM_BYPASS_COMPOSITOR -- X11: Prioritize _NET_WM_STATE_FULLSCREEN in true fullscreen mode +- X11: Prioritize _NET_WM_STATE_FULLSCREEN_ in true fullscreen mode - WIIU: Fix OOB read/write in keyboard driver. # 1.7.1 From a58f80cc090a8299f30afecd44926dc425a89581 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Wed, 25 Apr 2018 10:15:40 +0200 Subject: [PATCH 477/517] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 7e1d62cc86..6a84b56d14 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ - CHEEVOS: Support Atari 2600, Virtual Boy, and Arcade (only Neo Geo, CPS-1, CPS-2 and CPS-3 and only with fbalpha core). - CHEEVOS: Add option to automatically take a screenshot when an achievement is triggered. - CHEEVOS: Fixed incompatibilities with Neo Geo Pocket achievement sets. +- CHEEVOS: Store only login token, not password. - D3D10: Added D3D10 driver to release build. Has working shaders (Slang), overlay, and menu display driver support. Should be on par capabilities wise with D3D11 driver except for there being no hardware rendering right now. - D3D11: Experimental hardware renderer. Allows for libretro cores to use D3D11 for hardware rendering. First core to use this is PPSSPP. From fee49f0e7e2bb7a4172dd2b445d3a85b78ee0620 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 25 Apr 2018 10:21:26 +0200 Subject: [PATCH 478/517] Update libretro-common --- libretro-common/audio/audio_mixer.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libretro-common/audio/audio_mixer.c b/libretro-common/audio/audio_mixer.c index 4dffa95c84..7988ff6517 100644 --- a/libretro-common/audio/audio_mixer.c +++ b/libretro-common/audio/audio_mixer.c @@ -44,7 +44,7 @@ #define STB_VORBIS_NO_STDIO #define STB_VORBIS_NO_CRT -#include +#include #endif #ifdef HAVE_IBXM @@ -412,7 +412,7 @@ static bool audio_mixer_play_ogg( { stb_vorbis_info info; int res = 0; - float ratio = 0.0f; + float ratio = 1.0f; unsigned samples = 0; void *ogg_buffer = NULL; void *resampler_data = NULL; @@ -665,8 +665,8 @@ static void audio_mixer_mix_ogg(float* buffer, size_t num_frames, float volume) { int i; - struct resampler_data info; - float temp_buffer[AUDIO_MIXER_TEMP_OGG_BUFFER]; + struct resampler_data info = { 0 }; + float temp_buffer[AUDIO_MIXER_TEMP_OGG_BUFFER] = { 0 }; unsigned buf_free = (unsigned)(num_frames * 2); unsigned temp_samples = 0; float* pcm = NULL; @@ -704,7 +704,10 @@ again: info.output_frames = 0; info.ratio = voice->types.ogg.ratio; - voice->types.ogg.resampler->process(voice->types.ogg.resampler_data, &info); + if (voice->types.ogg.resampler) + voice->types.ogg.resampler->process(voice->types.ogg.resampler_data, &info); + else + memcpy(voice->types.ogg.buffer, temp_buffer, temp_samples * sizeof(float)); voice->types.ogg.position = 0; voice->types.ogg.samples = voice->types.ogg.buf_samples; } From 0737b90080d403ea7a099c177a3828fc28149827 Mon Sep 17 00:00:00 2001 From: Tatsuya79 Date: Wed, 25 Apr 2018 10:54:04 +0200 Subject: [PATCH 479/517] XMB fix crash with scale factor under 100 --- menu/drivers/xmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index c4a163daf1..57dffb84b5 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -4002,7 +4002,7 @@ static void *xmb_init(void **userdata, bool video_is_threaded) /* xmb_scale 50 = {2.5, 2.5, 2, 1.7, 2.5, 4, 2.4, 2.5} */ /* xmb_scale 75 = { 2, 1.6, 1.6, 1.4, 1.5, 2.3, 1.9, 1.3} */ - if (scale_value < 100 && xmb->use_ps3_layout) + if (scale_value < 100) { /* text length & word wrap (base 35 apply to file browser, 1st column) */ scale_mod[0] = -0.03 * scale_value + 4.083; From 7b1ad55b2079b631f7ff6946a267f53b5884c5e1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 25 Apr 2018 11:31:50 +0200 Subject: [PATCH 480/517] Try to use snprintf instead of sprintf --- frontend/drivers/platform_qnx.c | 8 ++- libretro-common/include/net/net_natt.h | 3 +- libretro-common/net/net_natt.c | 70 +++++++++++++++++--------- 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/frontend/drivers/platform_qnx.c b/frontend/drivers/platform_qnx.c index 0b3af7895b..ddd4829852 100644 --- a/frontend/drivers/platform_qnx.c +++ b/frontend/drivers/platform_qnx.c @@ -142,7 +142,9 @@ static void frontend_qnx_get_environment_settings(int *argc, char *argv[], file_path_str(FILE_PATH_MAIN_CONFIG), sizeof(g_defaults.path.config)); /* bundle copy */ - sprintf(data_assets_path, "%s/%s", data_path, "assets"); + snprintf(data_assets_path, + sizeof(data_assets_path), + "%s/%s", data_path, "assets"); if (!filestream_exists(data_assets_path)) { @@ -150,7 +152,9 @@ static void frontend_qnx_get_environment_settings(int *argc, char *argv[], RARCH_LOG( "Copying application assets to data directory...\n" ); - sprintf(copy_command, "cp -r %s/. %s", assets_path, data_path); + snprintf(copy_command, + sizeof(copy_command), + "cp -r %s/. %s", assets_path, data_path); if(system(copy_command) == -1) RARCH_LOG( "Asset copy failed: Shell could not be run.\n" ); diff --git a/libretro-common/include/net/net_natt.h b/libretro-common/include/net/net_natt.h index a755480808..66c3ff355a 100644 --- a/libretro-common/include/net/net_natt.h +++ b/libretro-common/include/net/net_natt.h @@ -30,7 +30,8 @@ RETRO_BEGIN_DECLS -struct natt_status { +struct natt_status +{ /** nfds for select when checking for input */ int nfds; diff --git a/libretro-common/net/net_natt.c b/libretro-common/net/net_natt.c index 1924f4d63c..33d0865208 100644 --- a/libretro-common/net/net_natt.c +++ b/libretro-common/net/net_natt.c @@ -74,8 +74,9 @@ void natt_init(void) descXML = (char *) miniwget(dev->descURL, &descXMLsize, 0, NULL); if (descXML) { - parserootdesc (descXML, descXMLsize, &data); - free (descXML); descXML = 0; + parserootdesc(descXML, descXMLsize, &data); + free (descXML); + descXML = 0; GetUPNPUrls (&urls, &data, dev->descURL, 0); } freeUPNPDevlist(devlist); @@ -112,44 +113,55 @@ static bool natt_open_port(struct natt_status *status, return false; /* figure out the internal info */ - if (getnameinfo(addr, addrlen, host, PATH_MAX_LENGTH, port_str, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0) + if (getnameinfo(addr, addrlen, host, PATH_MAX_LENGTH, + port_str, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0) return false; + proto_str = (proto == SOCKET_PROTOCOL_UDP) ? "UDP" : "TCP"; /* add the port mapping */ - r = UPNP_AddAnyPortMapping(urls.controlURL, data.first.servicetype, port_str, - port_str, host, "retroarch", proto_str, NULL, "3600", ext_port_str); + r = UPNP_AddAnyPortMapping(urls.controlURL, + data.first.servicetype, port_str, + port_str, host, "retroarch", + proto_str, NULL, "3600", ext_port_str); + if (r != 0) { /* try the older AddPortMapping */ memcpy(ext_port_str, port_str, 6); - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port_str, - port_str, host, "retroarch", proto_str, NULL, "3600"); + r = UPNP_AddPortMapping(urls.controlURL, + data.first.servicetype, port_str, + port_str, host, "retroarch", + proto_str, NULL, "3600"); } if (r != 0) return false; /* get the external IP */ - r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, ext_host); + r = UPNP_GetExternalIPAddress(urls.controlURL, + data.first.servicetype, ext_host); if (r != 0) return false; /* update the status */ - if (getaddrinfo_retro(ext_host, ext_port_str, &hints, &ext_addrinfo) != 0) + if (getaddrinfo_retro(ext_host, + ext_port_str, &hints, &ext_addrinfo) != 0) return false; if (ext_addrinfo->ai_family == AF_INET && ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in)) { - status->have_inet4 = true; - status->ext_inet4_addr = *((struct sockaddr_in *) ext_addrinfo->ai_addr); + status->have_inet4 = true; + status->ext_inet4_addr = *((struct sockaddr_in *) + ext_addrinfo->ai_addr); } #if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) else if (ext_addrinfo->ai_family == AF_INET6 && ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in6)) { - status->have_inet6 = true; - status->ext_inet6_addr = *((struct sockaddr_in6 *) ext_addrinfo->ai_addr); + status->have_inet6 = true; + status->ext_inet6_addr = *((struct sockaddr_in6 *) + ext_addrinfo->ai_addr); } #endif else @@ -169,16 +181,17 @@ static bool natt_open_port(struct natt_status *status, #endif } -bool natt_open_port_any(struct natt_status *status, uint16_t port, enum socket_protocol proto) +bool natt_open_port_any(struct natt_status *status, + uint16_t port, enum socket_protocol proto) { #if !defined(HAVE_SOCKET_LEGACY) && !defined(WIIU) - struct net_ifinfo list; - bool ret = false; size_t i; - struct addrinfo hints = {0}, *addr; char port_str[6]; + struct net_ifinfo list; + struct addrinfo hints = {0}, *addr; + bool ret = false; - sprintf(port_str, "%hu", port); + snprintf(port_str, sizeof(port_str), "%hu", port); /* get our interfaces */ if (!net_ifinfo_new(&list)) @@ -190,13 +203,15 @@ bool natt_open_port_any(struct natt_status *status, uint16_t port, enum socket_p struct net_ifinfo_entry *entry = list.entries + i; /* ignore localhost */ - if (string_is_equal(entry->host, "127.0.0.1") || string_is_equal(entry->host, "::1")) + if ( string_is_equal(entry->host, "127.0.0.1") || + string_is_equal(entry->host, "::1")) continue; /* make a request for this host */ if (getaddrinfo_retro(entry->host, port_str, &hints, &addr) == 0) { - ret = natt_open_port(status, addr->ai_addr, addr->ai_addrlen, proto) || ret; + ret = natt_open_port(status, addr->ai_addr, + addr->ai_addrlen, proto) || ret; freeaddrinfo_retro(addr); } } @@ -218,19 +233,24 @@ bool natt_read(struct natt_status *status) } #if 0 -/* If we want to remove redirects in the future, this is a sample of how to do - * that */ +/* If we want to remove redirects in the future, this is a + * sample of how to do that. */ + void upnp_rem_redir (int port) { - char port_str[16]; int t; + char port_str[16]; + printf("TB : upnp_rem_redir (%d)\n", port); + if(urls.controlURL[0] == '\0') { printf("TB : the init was not done !\n"); return; } - sprintf(port_str, "%d", port); - UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port_str, "TCP", NULL); + + snprintf(port_str, sizeof(port_str), "%d", port); + UPNP_DeletePortMapping(urls.controlURL, + data.first.servicetype, port_str, "TCP", NULL); } #endif From 9a7ca5fe57092ccd7a1e31d8dbdca78c55942085 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 25 Apr 2018 12:02:35 +0200 Subject: [PATCH 481/517] C89 buildfix --- cheevos/cheevos.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 046bf53620..1ac0bb183d 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -3644,6 +3644,8 @@ found: if (coro->json) { char error_response[64]; + char error_message[256]; + cheevos_get_value( coro->json, CHEEVOS_JSON_KEY_ERROR, @@ -3689,7 +3691,6 @@ found: free((void*)coro->json); /* Site returned error, display it */ - char error_message[256]; snprintf(error_message, sizeof(error_message), "RetroAchievements: %s", error_response); From 023e28031909222765d248f38d9b98d764597e96 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 25 Apr 2018 14:20:37 +0200 Subject: [PATCH 482/517] win32_get_refresh_rate not available on Xbox --- gfx/drivers/d3d8.c | 4 ++++ gfx/drivers/d3d9.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index c39a70cd3c..11d19f9c9f 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -1884,7 +1884,11 @@ static const video_poke_interface_t d3d_poke_interface = { d3d8_load_texture, d3d8_unload_texture, d3d8_set_video_mode, +#ifdef _XBOX + NULL, +#else win32_get_refresh_rate, +#endif NULL, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index 5902a65612..61b610ed4a 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -1911,7 +1911,10 @@ static const video_poke_interface_t d3d9_poke_interface = { d3d9_load_texture, d3d9_unload_texture, d3d9_set_video_mode, +#ifdef _XBOX +#else win32_get_refresh_rate, +#endif NULL, NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ From 029ba3d2f67a8cc9165bae708def380ecf6626e6 Mon Sep 17 00:00:00 2001 From: Andre Leiradella Date: Wed, 25 Apr 2018 17:56:15 +0100 Subject: [PATCH 483/517] Mute cheevos logging, define CHEEVOS_VERBOSE in cheevos.h to enable --- cheevos/cheevos.c | 176 ++++++++++++++++++++++------------------------ cheevos/cheevos.h | 17 +++++ cheevos/cond.c | 2 +- cheevos/var.c | 13 +--- 4 files changed, 103 insertions(+), 105 deletions(-) diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 1ac0bb183d..4cdb5f7f95 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -310,7 +310,7 @@ static unsigned size_in_megabytes(unsigned val) static void cheevos_log_url(const char* format, const char* url) { #ifdef CHEEVOS_LOG_PASSWORD - RARCH_LOG(format, url); + CHEEVOS_LOG(format, url); #else char copy[256]; char* aux = NULL; @@ -361,7 +361,7 @@ static void cheevos_log_url(const char* format, const char* url) *aux = 0; } - RARCH_LOG(format, copy); + CHEEVOS_LOG(format, copy); #endif } #endif @@ -424,7 +424,7 @@ static void cheevos_log_var(const cheevos_var_t* var) if (!var) return; - RARCH_LOG("[CHEEVOS]: size: %s\n", + CHEEVOS_LOG("[CHEEVOS]: size: %s\n", var->size == CHEEVOS_VAR_SIZE_BIT_0 ? "bit 0" : var->size == CHEEVOS_VAR_SIZE_BIT_1 ? "bit 1" : var->size == CHEEVOS_VAR_SIZE_BIT_2 ? "bit 2" : @@ -440,14 +440,14 @@ static void cheevos_log_var(const cheevos_var_t* var) var->size == CHEEVOS_VAR_SIZE_THIRTYTWO_BITS ? "dword" : "?" ); - RARCH_LOG("[CHEEVOS]: type: %s\n", + CHEEVOS_LOG("[CHEEVOS]: type: %s\n", var->type == CHEEVOS_VAR_TYPE_ADDRESS ? "address" : var->type == CHEEVOS_VAR_TYPE_VALUE_COMP ? "value" : var->type == CHEEVOS_VAR_TYPE_DELTA_MEM ? "delta" : var->type == CHEEVOS_VAR_TYPE_DYNAMIC_VAR ? "dynamic" : "?" ); - RARCH_LOG("[CHEEVOS]: value: %u\n", var->value); + CHEEVOS_LOG("[CHEEVOS]: value: %u\n", var->value); } static void cheevos_log_cond(const cheevos_cond_t* cond) @@ -455,8 +455,8 @@ static void cheevos_log_cond(const cheevos_cond_t* cond) if (!cond) return; - RARCH_LOG("[CHEEVOS]: condition %p\n", cond); - RARCH_LOG("[CHEEVOS]: type: %s\n", + CHEEVOS_LOG("[CHEEVOS]: condition %p\n", cond); + CHEEVOS_LOG("[CHEEVOS]: type: %s\n", cond->type == CHEEVOS_COND_TYPE_STANDARD ? "standard" : cond->type == CHEEVOS_COND_TYPE_PAUSE_IF ? "pause" : cond->type == CHEEVOS_COND_TYPE_RESET_IF ? "reset" : @@ -465,10 +465,10 @@ static void cheevos_log_cond(const cheevos_cond_t* cond) cond->type == CHEEVOS_COND_TYPE_ADD_HITS ? "add hits" : "?" ); - RARCH_LOG("[CHEEVOS]: req_hits: %u\n", cond->req_hits); - RARCH_LOG("[CHEEVOS]: source:\n"); + CHEEVOS_LOG("[CHEEVOS]: req_hits: %u\n", cond->req_hits); + CHEEVOS_LOG("[CHEEVOS]: source:\n"); cheevos_log_var(&cond->source); - RARCH_LOG("[CHEEVOS]: op: %s\n", + CHEEVOS_LOG("[CHEEVOS]: op: %s\n", cond->op == CHEEVOS_COND_OP_EQUALS ? "==" : cond->op == CHEEVOS_COND_OP_LESS_THAN ? "<" : cond->op == CHEEVOS_COND_OP_LESS_THAN_OR_EQUAL ? "<=" : @@ -477,7 +477,7 @@ static void cheevos_log_cond(const cheevos_cond_t* cond) cond->op == CHEEVOS_COND_OP_NOT_EQUAL_TO ? "!=" : "?" ); - RARCH_LOG("[CHEEVOS]: target:\n"); + CHEEVOS_LOG("[CHEEVOS]: target:\n"); cheevos_log_var(&cond->target); } @@ -487,15 +487,15 @@ static void cheevos_log_cheevo(const cheevo_t* cheevo, if (!cheevo || !memaddr_ud) return; - RARCH_LOG("[CHEEVOS]: cheevo %p\n", cheevo); - RARCH_LOG("[CHEEVOS]: id: %u\n", cheevo->id); - RARCH_LOG("[CHEEVOS]: title: %s\n", cheevo->title); - RARCH_LOG("[CHEEVOS]: desc: %s\n", cheevo->description); - RARCH_LOG("[CHEEVOS]: author: %s\n", cheevo->author); - RARCH_LOG("[CHEEVOS]: badge: %s\n", cheevo->badge); - RARCH_LOG("[CHEEVOS]: points: %u\n", cheevo->points); - RARCH_LOG("[CHEEVOS]: sets: TBD\n"); - RARCH_LOG("[CHEEVOS]: memaddr: %.*s\n", + CHEEVOS_LOG("[CHEEVOS]: cheevo %p\n", cheevo); + CHEEVOS_LOG("[CHEEVOS]: id: %u\n", cheevo->id); + CHEEVOS_LOG("[CHEEVOS]: title: %s\n", cheevo->title); + CHEEVOS_LOG("[CHEEVOS]: desc: %s\n", cheevo->description); + CHEEVOS_LOG("[CHEEVOS]: author: %s\n", cheevo->author); + CHEEVOS_LOG("[CHEEVOS]: badge: %s\n", cheevo->badge); + CHEEVOS_LOG("[CHEEVOS]: points: %u\n", cheevo->points); + CHEEVOS_LOG("[CHEEVOS]: sets: TBD\n"); + CHEEVOS_LOG("[CHEEVOS]: memaddr: %.*s\n", (int)memaddr_ud->length, memaddr_ud->string); } @@ -651,7 +651,7 @@ static void cheevos_post_log_cheevo(const cheevo_t* cheevo) if (!cheevo) return; cheevos_build_memaddr(&cheevo->condition, memaddr, sizeof(memaddr)); - RARCH_LOG("[CHEEVOS]: memaddr (computed): %s\n", memaddr); + CHEEVOS_LOG("[CHEEVOS]: memaddr (computed): %s\n", memaddr); } static void cheevos_log_lboard(const cheevos_leaderboard_t* lb) @@ -664,19 +664,19 @@ static void cheevos_log_lboard(const cheevos_leaderboard_t* lb) if (!lb) return; - RARCH_LOG("[CHEEVOS]: leaderboard %p\n", lb); - RARCH_LOG("[CHEEVOS]: id: %u\n", lb->id); - RARCH_LOG("[CHEEVOS]: title: %s\n", lb->title); - RARCH_LOG("[CHEEVOS]: desc: %s\n", lb->description); + CHEEVOS_LOG("[CHEEVOS]: leaderboard %p\n", lb); + CHEEVOS_LOG("[CHEEVOS]: id: %u\n", lb->id); + CHEEVOS_LOG("[CHEEVOS]: title: %s\n", lb->title); + CHEEVOS_LOG("[CHEEVOS]: desc: %s\n", lb->description); cheevos_build_memaddr(&lb->start, mem, sizeof(mem)); - RARCH_LOG("[CHEEVOS]: start: %s\n", mem); + CHEEVOS_LOG("[CHEEVOS]: start: %s\n", mem); cheevos_build_memaddr(&lb->cancel, mem, sizeof(mem)); - RARCH_LOG("[CHEEVOS]: cancel: %s\n", mem); + CHEEVOS_LOG("[CHEEVOS]: cancel: %s\n", mem); cheevos_build_memaddr(&lb->submit, mem, sizeof(mem)); - RARCH_LOG("[CHEEVOS]: submit: %s\n", mem); + CHEEVOS_LOG("[CHEEVOS]: submit: %s\n", mem); left = sizeof(mem); aux = mem; @@ -691,7 +691,7 @@ static void cheevos_log_lboard(const cheevos_leaderboard_t* lb) cheevos_add_int(&aux, &left, lb->value.terms[i].multiplier); } - RARCH_LOG("[CHEEVOS]: value: %s\n", mem); + CHEEVOS_LOG("[CHEEVOS]: value: %s\n", mem); } #endif @@ -964,10 +964,8 @@ static int cheevos_parse_condition( cheevos_cond_count_in_set(memaddr, set); condset->conds = NULL; -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: set %p (index=%u)\n", condset, set); - RARCH_LOG("[CHEEVOS]: conds: %u\n", condset->count); -#endif + CHEEVOS_LOG("[CHEEVOS]: set %p (index=%u)\n", condset, set); + CHEEVOS_LOG("[CHEEVOS]: conds: %u\n", condset->count); if (condset->count) { @@ -1896,14 +1894,14 @@ static void cheevos_unlocked(void *task_data, void *user_data, if (!error) { - RARCH_LOG("[CHEEVOS]: awarded achievement %u.\n", cheevo->id); + CHEEVOS_LOG("[CHEEVOS]: awarded achievement %u.\n", cheevo->id); } else { char url[256]; url[0] = '\0'; - RARCH_ERR("[CHEEVOS]: error awarding achievement %u, retrying...\n", cheevo->id); + CHEEVOS_ERR("[CHEEVOS]: error awarding achievement %u, retrying...\n", cheevo->id); cheevos_make_unlock_url(cheevo, url, sizeof(url)); task_push_http_transfer(url, true, NULL, cheevos_unlocked, cheevo); @@ -1951,7 +1949,7 @@ static void cheevos_test_cheevo_set(const cheevoset_t *set) if (mode == CHEEVOS_ACTIVE_HARDCORE) cheevo->active &= ~CHEEVOS_ACTIVE_SOFTCORE; - RARCH_LOG("[CHEEVOS]: awarding cheevo %u: %s (%s).\n", + CHEEVOS_LOG("[CHEEVOS]: awarding cheevo %u: %s (%s).\n", cheevo->id, cheevo->title, cheevo->description); snprintf(msg, sizeof(msg), "Achievement Unlocked: %s", @@ -1976,9 +1974,9 @@ static void cheevos_test_cheevo_set(const cheevoset_t *set) if (take_screenshot(shotname, true, video_driver_cached_frame_has_valid_framebuffer())) - RARCH_LOG("[CHEEVOS]: got a screenshot for cheevo %u\n", cheevo->id); + CHEEVOS_LOG("[CHEEVOS]: got a screenshot for cheevo %u\n", cheevo->id); else - RARCH_LOG("[CHEEVOS]: failed to get screenshot for cheevo %u\n", cheevo->id); + CHEEVOS_LOG("[CHEEVOS]: failed to get screenshot for cheevo %u\n", cheevo->id); } } @@ -2044,7 +2042,7 @@ static int cheevos_expr_value(cheevos_expr_t* expr) if (expr->compare_count >= ARRAY_SIZE(values)) { - RARCH_ERR("[CHEEVOS]: too many values in the leaderboard expression: %u\n", expr->compare_count); + CHEEVOS_ERR("[CHEEVOS]: too many values in the leaderboard expression: %u\n", expr->compare_count); return 0; } @@ -2054,7 +2052,7 @@ static int cheevos_expr_value(cheevos_expr_t* expr) { if (current_value >= ARRAY_SIZE(values)) { - RARCH_ERR("[CHEEVOS]: too many values in the leaderboard expression: %u\n", current_value); + CHEEVOS_ERR("[CHEEVOS]: too many values in the leaderboard expression: %u\n", current_value); return 0; } @@ -2128,11 +2126,11 @@ static void cheevos_lboard_submit(void *task_data, void *user_data, if (!error) { - RARCH_ERR("[CHEEVOS]: error submitting leaderboard %u\n", lboard->id); + CHEEVOS_ERR("[CHEEVOS]: error submitting leaderboard %u\n", lboard->id); return; } - RARCH_LOG("[CHEEVOS]: submitted leaderboard %u.\n", lboard->id); + CHEEVOS_LOG("[CHEEVOS]: submitted leaderboard %u.\n", lboard->id); } static void cheevos_test_leaderboards(void) @@ -2151,10 +2149,8 @@ static void cheevos_test_leaderboards(void) if (value != lboard->last_value) { -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: value lboard %s %u\n", + CHEEVOS_LOG("[CHEEVOS]: value lboard %s %u\n", lboard->title, value); -#endif lboard->last_value = value; } @@ -2165,7 +2161,7 @@ static void cheevos_test_leaderboards(void) /* failsafe for improper LBs */ if (value == 0) { - RARCH_LOG("[CHEEVOS]: error: lboard %s tried to submit 0\n", + CHEEVOS_LOG("[CHEEVOS]: error: lboard %s tried to submit 0\n", lboard->title); runloop_msg_queue_push("Leaderboard attempt cancelled!", 0, 2 * 60, false); @@ -2179,7 +2175,7 @@ static void cheevos_test_leaderboards(void) cheevos_make_lboard_url(lboard, url, sizeof(url)); task_push_http_transfer(url, true, NULL, cheevos_lboard_submit, lboard); - RARCH_LOG("[CHEEVOS]: submit lboard %s\n", lboard->title); + CHEEVOS_LOG("[CHEEVOS]: submit lboard %s\n", lboard->title); cheevos_format_value(value, lboard->format, formatted_value, sizeof(formatted_value)); @@ -2192,7 +2188,7 @@ static void cheevos_test_leaderboards(void) if (cheevos_test_lboard_condition(&lboard->cancel)) { - RARCH_LOG("[CHEEVOS]: cancel lboard %s\n", lboard->title); + CHEEVOS_LOG("[CHEEVOS]: cancel lboard %s\n", lboard->title); lboard->active = 0; runloop_msg_queue_push("Leaderboard attempt cancelled!", 0, 2 * 60, false); @@ -2204,7 +2200,7 @@ static void cheevos_test_leaderboards(void) { char msg[256]; - RARCH_LOG("[CHEEVOS]: start lboard %s\n", lboard->title); + CHEEVOS_LOG("[CHEEVOS]: start lboard %s\n", lboard->title); lboard->active = 1; lboard->last_value = -1; @@ -2317,10 +2313,10 @@ static int cheevos_deactivate__json_number(void *userdata, } if (found) - RARCH_LOG("[CHEEVOS]: deactivated unlocked cheevo %u (%s).\n", + CHEEVOS_LOG("[CHEEVOS]: deactivated unlocked cheevo %u (%s).\n", cheevo->id, cheevo->title); else - RARCH_ERR("[CHEEVOS]: unknown cheevo to deactivate: %u.\n", id); + CHEEVOS_ERR("[CHEEVOS]: unknown cheevo to deactivate: %u.\n", id); } return 0; @@ -2516,9 +2512,7 @@ bool cheevos_unload(void) if (running) { -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: Asked the load thread to terminate\n"); -#endif + CHEEVOS_LOG("[CHEEVOS]: Asked the load thread to terminate\n"); task_queue_cancel_task(cheevos_locals.task); #ifdef HAVE_THREADS @@ -2568,7 +2562,7 @@ bool cheevos_toggle_hardcore_mode(void) if (settings->bools.rewind_enable) command_event(CMD_EVENT_REWIND_DEINIT, NULL); - RARCH_LOG("%s\n", msg); + CHEEVOS_LOG("%s\n", msg); runloop_msg_queue_push(msg, 0, 3 * 60, true); } else @@ -2612,7 +2606,7 @@ static void cheevos_patch_addresses(cheevoset_t* set) cheevos_var_patch_addr(&cond->source, cheevos_locals.console_id); #ifdef CHEEVOS_DUMP_ADDRS - RARCH_LOG("[CHEEVOS]: s-var %03d:%08X\n", + CHEEVOS_LOG("[CHEEVOS]: s-var %03d:%08X\n", cond->source.bank_id + 1, cond->source.value); #endif break; @@ -2628,7 +2622,7 @@ static void cheevos_patch_addresses(cheevoset_t* set) cheevos_var_patch_addr(&cond->target, cheevos_locals.console_id); #ifdef CHEEVOS_DUMP_ADDRS - RARCH_LOG("[CHEEVOS]: t-var %03d:%08X\n", + CHEEVOS_LOG("[CHEEVOS]: t-var %03d:%08X\n", cond->target.bank_id + 1, cond->target.value); #endif break; @@ -2665,7 +2659,7 @@ static void cheevos_patch_lb_conditions(cheevos_condition_t* condition) cheevos_var_patch_addr(&cond->source, cheevos_locals.console_id); #ifdef CHEEVOS_DUMP_ADDRS - RARCH_LOG("[CHEEVOS]: s-var %03d:%08X\n", + CHEEVOS_LOG("[CHEEVOS]: s-var %03d:%08X\n", cond->source.bank_id + 1, cond->source.value); #endif break; @@ -2679,7 +2673,7 @@ static void cheevos_patch_lb_conditions(cheevos_condition_t* condition) cheevos_var_patch_addr(&cond->target, cheevos_locals.console_id); #ifdef CHEEVOS_DUMP_ADDRS - RARCH_LOG("[CHEEVOS]: t-var %03d:%08X\n", + CHEEVOS_LOG("[CHEEVOS]: t-var %03d:%08X\n", cond->target.bank_id + 1, cond->target.value); #endif break; @@ -2708,7 +2702,7 @@ static void cheevos_patch_lb_expressions(cheevos_expr_t* expression) case CHEEVOS_VAR_TYPE_DELTA_MEM: cheevos_var_patch_addr(&term->var, cheevos_locals.console_id); #ifdef CHEEVOS_DUMP_ADDRS - RARCH_LOG("[CHEEVOS]: s-var %03d:%08X\n", + CHEEVOS_LOG("[CHEEVOS]: s-var %03d:%08X\n", term->var.bank_id + 1, term->var.value); #endif break; @@ -2927,16 +2921,16 @@ static int cheevos_iterate(coro_t *coro) cheevos_locals.meminfo[3].id = RETRO_MEMORY_RTC; core_get_memory(&cheevos_locals.meminfo[3]); - RARCH_LOG("[CHEEVOS]: system RAM: %p %u\n", + CHEEVOS_LOG("[CHEEVOS]: system RAM: %p %u\n", cheevos_locals.meminfo[0].data, cheevos_locals.meminfo[0].size); - RARCH_LOG("[CHEEVOS]: save RAM: %p %u\n", + CHEEVOS_LOG("[CHEEVOS]: save RAM: %p %u\n", cheevos_locals.meminfo[1].data, cheevos_locals.meminfo[1].size); - RARCH_LOG("[CHEEVOS]: video RAM: %p %u\n", + CHEEVOS_LOG("[CHEEVOS]: video RAM: %p %u\n", cheevos_locals.meminfo[2].data, cheevos_locals.meminfo[2].size); - RARCH_LOG("[CHEEVOS]: RTC: %p %u\n", + CHEEVOS_LOG("[CHEEVOS]: RTC: %p %u\n", cheevos_locals.meminfo[3].data, cheevos_locals.meminfo[3].size); @@ -3034,7 +3028,7 @@ static int cheevos_iterate(coro_t *coro) { if (finders[coro->i].ext_hashes[coro->j] == hash) { - RARCH_LOG("[CHEEVOS]: testing %s.\n", + CHEEVOS_LOG("[CHEEVOS]: testing %s.\n", finders[coro->i].name); /* @@ -3059,7 +3053,7 @@ static int cheevos_iterate(coro_t *coro) if (finders[coro->i].ext_hashes) continue; - RARCH_LOG("[CHEEVOS]: testing %s.\n", + CHEEVOS_LOG("[CHEEVOS]: testing %s.\n", finders[coro->i].name); /* @@ -3072,7 +3066,7 @@ static int cheevos_iterate(coro_t *coro) goto found; } - RARCH_LOG("[CHEEVOS]: this game doesn't feature achievements.\n"); + CHEEVOS_LOG("[CHEEVOS]: this game doesn't feature achievements.\n"); CORO_STOP(); found: @@ -3098,7 +3092,7 @@ found: if (!coro->json) { runloop_msg_queue_push("Error loading achievements.", 0, 5 * 60, false); - RARCH_ERR("[CHEEVOS]: error loading achievements.\n"); + CHEEVOS_ERR("[CHEEVOS]: error loading achievements.\n"); CORO_STOP(); } #endif @@ -3428,7 +3422,7 @@ found: { char gameid[16]; - RARCH_LOG( + CHEEVOS_LOG( "[CHEEVOS]: getting game id for hash %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", coro->hash[ 0], coro->hash[ 1], coro->hash[ 2], coro->hash[ 3], coro->hash[ 4], coro->hash[ 5], coro->hash[ 6], coro->hash[ 7], @@ -3461,13 +3455,13 @@ found: { if ((void*)coro->json) free((void*)coro->json); - RARCH_ERR("[CHEEVOS]: error getting game_id.\n"); + CHEEVOS_ERR("[CHEEVOS]: error getting game_id.\n"); CORO_RET(); } if ((void*)coro->json) free((void*)coro->json); - RARCH_LOG("[CHEEVOS]: got game id %s.\n", gameid); + CHEEVOS_LOG("[CHEEVOS]: got game id %s.\n", gameid); coro->gameid = (unsigned)strtol(gameid, NULL, 10); CORO_RET(); } @@ -3497,11 +3491,11 @@ found: if (!coro->json) { - RARCH_ERR("[CHEEVOS]: error getting achievements for game id %u.\n", coro->gameid); + CHEEVOS_ERR("[CHEEVOS]: error getting achievements for game id %u.\n", coro->gameid); CORO_STOP(); } - RARCH_LOG("[CHEEVOS]: got achievements for game id %u.\n", coro->gameid); + CHEEVOS_LOG("[CHEEVOS]: got achievements for game id %u.\n", coro->gameid); CORO_RET(); /************************************************************************** @@ -3554,7 +3548,7 @@ found: if (!badge_exists(coro->badge_fullpath)) { #ifdef CHEEVOS_LOG_BADGES - RARCH_LOG( + CHEEVOS_LOG( "[CHEEVOS]: downloading badge %s\n", coro->badge_fullpath); #endif @@ -3569,7 +3563,7 @@ found: { if (!filestream_write_file(coro->badge_fullpath, coro->json, coro->k)) - RARCH_ERR("[CHEEVOS]: error writing badge %s\n", coro->badge_fullpath); + CHEEVOS_ERR("[CHEEVOS]: error writing badge %s\n", coro->badge_fullpath); else free(coro->json); } @@ -3618,7 +3612,7 @@ found: runloop_msg_queue_push( "Please fill in your account information in Settings.", 0, 5 * 60, false); - RARCH_ERR("[CHEEVOS]: login info not informed.\n"); + CHEEVOS_ERR("[CHEEVOS]: login info not informed.\n"); CORO_STOP(); } @@ -3702,7 +3696,7 @@ found: } runloop_msg_queue_push("RetroAchievements: Error contacting server.", 0, 5 * 60, false); - RARCH_ERR("[CHEEVOS]: error getting user token.\n"); + CHEEVOS_ERR("[CHEEVOS]: error getting user token.\n"); CORO_STOP(); @@ -3734,7 +3728,7 @@ found: for (coro->k = 0; coro->k < 5; coro->k++) { if (coro->k != 0) - RARCH_LOG("[CHEEVOS]: Retrying HTTP request: %u of 5\n", coro->k + 1); + CHEEVOS_LOG("[CHEEVOS]: Retrying HTTP request: %u of 5\n", coro->k + 1); coro->json = NULL; coro->conn = net_http_connection_new( @@ -3796,7 +3790,7 @@ found: net_http_connection_free(coro->conn); } - RARCH_LOG("[CHEEVOS]: Couldn't connect to server after 5 tries\n"); + CHEEVOS_LOG("[CHEEVOS]: Couldn't connect to server after 5 tries\n"); CORO_RET(); /************************************************************************** @@ -3828,15 +3822,15 @@ found: if (coro->json) { if (!cheevos_deactivate_unlocks(coro->json, CHEEVOS_ACTIVE_SOFTCORE)) - RARCH_LOG("[CHEEVOS]: deactivated unlocked achievements in softcore mode.\n"); + CHEEVOS_LOG("[CHEEVOS]: deactivated unlocked achievements in softcore mode.\n"); else - RARCH_ERR("[CHEEVOS]: error deactivating unlocked achievements in softcore mode.\n"); + CHEEVOS_ERR("[CHEEVOS]: error deactivating unlocked achievements in softcore mode.\n"); if ((void*)coro->json) free((void*)coro->json); } else - RARCH_ERR("[CHEEVOS]: error retrieving list of unlocked achievements in softcore mode.\n"); + CHEEVOS_ERR("[CHEEVOS]: error retrieving list of unlocked achievements in softcore mode.\n"); /* Deactivate achievements in hardcore mode. */ snprintf( @@ -3857,15 +3851,15 @@ found: if (coro->json) { if (!cheevos_deactivate_unlocks(coro->json, CHEEVOS_ACTIVE_HARDCORE)) - RARCH_LOG("[CHEEVOS]: deactivated unlocked achievements in hardcore mode.\n"); + CHEEVOS_LOG("[CHEEVOS]: deactivated unlocked achievements in hardcore mode.\n"); else - RARCH_ERR("[CHEEVOS]: error deactivating unlocked achievements in hardcore mode.\n"); + CHEEVOS_ERR("[CHEEVOS]: error deactivating unlocked achievements in hardcore mode.\n"); if ((void*)coro->json) free((void*)coro->json); } else - RARCH_ERR("[CHEEVOS]: error retrieving list of unlocked achievements in hardcore mode.\n"); + CHEEVOS_ERR("[CHEEVOS]: error retrieving list of unlocked achievements in hardcore mode.\n"); #endif CORO_RET(); @@ -3894,14 +3888,14 @@ found: if (coro->json) { - RARCH_LOG("[CHEEVOS]: posted playing activity.\n"); + CHEEVOS_LOG("[CHEEVOS]: posted playing activity.\n"); if ((void*)coro->json) free((void*)coro->json); } else - RARCH_ERR("[CHEEVOS]: error posting playing activity.\n"); + CHEEVOS_ERR("[CHEEVOS]: error posting playing activity.\n"); - RARCH_LOG("[CHEEVOS]: posted playing activity.\n"); + CHEEVOS_LOG("[CHEEVOS]: posted playing activity.\n"); CORO_RET(); CORO_LEAVE(); @@ -3922,16 +3916,14 @@ static void cheevos_task_handler(retro_task_t *task) cheevos_locals.task = NULL; CHEEVOS_UNLOCK(cheevos_locals.task_lock); -#ifdef CHEEVOS_VERBOSE if (task_get_cancelled(task)) { - RARCH_LOG("[CHEEVOS]: Load task cancelled\n"); + CHEEVOS_LOG("[CHEEVOS]: Load task cancelled\n"); } else { - RARCH_LOG("[CHEEVOS]: Load task finished\n"); + CHEEVOS_LOG("[CHEEVOS]: Load task finished\n"); } -#endif if (coro->data) free(coro->data); diff --git a/cheevos/cheevos.h b/cheevos/cheevos.h index dd070ef441..0bcb4894f7 100644 --- a/cheevos/cheevos.h +++ b/cheevos/cheevos.h @@ -38,6 +38,23 @@ End of setup #define CHEEVOS_TAG "[CHEEVOS]: " +#ifdef CHEEVOS_VERBOSE + +#define CHEEVOS_LOG RARCH_LOG +#define CHEEVOS_ERR RARCH_ERR + +#else + +static void STUB_LOG(const char *fmt, ...) +{ + (void)fmt; +} + +#define CHEEVOS_LOG STUB_LOG +#define CHEEVOS_ERR STUB_LOG + +#endif + typedef struct cheevos_ctx_desc { unsigned idx; diff --git a/cheevos/cond.c b/cheevos/cond.c index 3566f8e6bf..c9b54b619b 100644 --- a/cheevos/cond.c +++ b/cheevos/cond.c @@ -65,7 +65,7 @@ static cheevos_cond_op_t cheevos_cond_parse_operator(const char** memaddr) } else { - RARCH_ERR(CHEEVOS_TAG "unknown operator %c\n.", *str); + CHEEVOS_ERR(CHEEVOS_TAG "unknown operator %c\n.", *str); op = CHEEVOS_COND_OP_EQUALS; } diff --git a/cheevos/var.c b/cheevos/var.c index e0e6d5c6d8..5f13c2b8b6 100644 --- a/cheevos/var.c +++ b/cheevos/var.c @@ -24,17 +24,6 @@ #include "../core.h" #include "../verbosity.h" -static void STUB_LOG(const char *fmt, ...) -{ - (void)fmt; -} - -#ifdef CHEEVOS_VERBOSE -#define CHEEVOS_LOG RARCH_LOG -#else -#define CHEEVOS_LOG STUB_LOG -#endif - /***************************************************************************** Parsing *****************************************************************************/ @@ -327,7 +316,7 @@ uint8_t* cheevos_var_get_memory(const cheevos_var_t* var) meminfo.id = RETRO_MEMORY_RTC; break; default: - RARCH_ERR(CHEEVOS_TAG "invalid bank id: %s\n", var->bank_id); + CHEEVOS_ERR(CHEEVOS_TAG "invalid bank id: %s\n", var->bank_id); break; } From f04548a95dff95862572e9feef11cbcd43366750 Mon Sep 17 00:00:00 2001 From: Andre Leiradella Date: Wed, 25 Apr 2018 18:02:31 +0100 Subject: [PATCH 484/517] Fixed warning about unused STUB_LOG; removed unused debug functions --- cheevos/cheevos.c | 350 ++-------------------------------------------- cheevos/cheevos.h | 9 +- cheevos/cond.c | 3 - 3 files changed, 12 insertions(+), 350 deletions(-) diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 4cdb5f7f95..c4da205828 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -301,6 +301,15 @@ int cheats_were_enabled = 0; Supporting functions. *****************************************************************************/ +#ifndef CHEEVOS_VERBOSE + +void cheevos_log(const char *fmt, ...) +{ + (void)fmt; +} + +#endif + static unsigned size_in_megabytes(unsigned val) { return (val * 1024 * 1024); @@ -366,335 +375,6 @@ static void cheevos_log_url(const char* format, const char* url) } #endif -#ifdef CHEEVOS_VERBOSE -static void cheevos_add_char(char** aux, size_t* left, char k) -{ - if (*left >= 1) - { - **aux = k; - (*aux)++; - (*left)--; - } -} - -static void cheevos_add_string(char** aux, size_t* left, const char* s) -{ - size_t len = strlen(s); - - if (*left >= len) - { - strcpy(*aux, s); - *aux += len; - *left -= len; - } -} - -static void cheevos_add_hex(char** aux, size_t* left, unsigned v) -{ - char buffer[32]; - - snprintf(buffer, sizeof(buffer), "%06x", v); - buffer[sizeof(buffer) - 1] = 0; - - cheevos_add_string(aux, left, buffer); -} - -static void cheevos_add_uint(char** aux, size_t* left, unsigned v) -{ - char buffer[32]; - - snprintf(buffer, sizeof(buffer), "%u", v); - buffer[sizeof(buffer) - 1] = 0; - - cheevos_add_string(aux, left, buffer); -} - -static void cheevos_add_int(char** aux, size_t* left, int v) -{ - char buffer[32]; - - snprintf(buffer, sizeof(buffer), "%d", v); - buffer[sizeof(buffer) - 1] = 0; - - cheevos_add_string(aux, left, buffer); -} - -static void cheevos_log_var(const cheevos_var_t* var) -{ - if (!var) - return; - - CHEEVOS_LOG("[CHEEVOS]: size: %s\n", - var->size == CHEEVOS_VAR_SIZE_BIT_0 ? "bit 0" : - var->size == CHEEVOS_VAR_SIZE_BIT_1 ? "bit 1" : - var->size == CHEEVOS_VAR_SIZE_BIT_2 ? "bit 2" : - var->size == CHEEVOS_VAR_SIZE_BIT_3 ? "bit 3" : - var->size == CHEEVOS_VAR_SIZE_BIT_4 ? "bit 4" : - var->size == CHEEVOS_VAR_SIZE_BIT_5 ? "bit 5" : - var->size == CHEEVOS_VAR_SIZE_BIT_6 ? "bit 6" : - var->size == CHEEVOS_VAR_SIZE_BIT_7 ? "bit 7" : - var->size == CHEEVOS_VAR_SIZE_NIBBLE_LOWER ? "low nibble" : - var->size == CHEEVOS_VAR_SIZE_NIBBLE_UPPER ? "high nibble" : - var->size == CHEEVOS_VAR_SIZE_EIGHT_BITS ? "byte" : - var->size == CHEEVOS_VAR_SIZE_SIXTEEN_BITS ? "word" : - var->size == CHEEVOS_VAR_SIZE_THIRTYTWO_BITS ? "dword" : - "?" - ); - CHEEVOS_LOG("[CHEEVOS]: type: %s\n", - var->type == CHEEVOS_VAR_TYPE_ADDRESS ? "address" : - var->type == CHEEVOS_VAR_TYPE_VALUE_COMP ? "value" : - var->type == CHEEVOS_VAR_TYPE_DELTA_MEM ? "delta" : - var->type == CHEEVOS_VAR_TYPE_DYNAMIC_VAR ? "dynamic" : - "?" - ); - CHEEVOS_LOG("[CHEEVOS]: value: %u\n", var->value); -} - -static void cheevos_log_cond(const cheevos_cond_t* cond) -{ - if (!cond) - return; - - CHEEVOS_LOG("[CHEEVOS]: condition %p\n", cond); - CHEEVOS_LOG("[CHEEVOS]: type: %s\n", - cond->type == CHEEVOS_COND_TYPE_STANDARD ? "standard" : - cond->type == CHEEVOS_COND_TYPE_PAUSE_IF ? "pause" : - cond->type == CHEEVOS_COND_TYPE_RESET_IF ? "reset" : - cond->type == CHEEVOS_COND_TYPE_ADD_SOURCE ? "add source" : - cond->type == CHEEVOS_COND_TYPE_SUB_SOURCE ? "sub source" : - cond->type == CHEEVOS_COND_TYPE_ADD_HITS ? "add hits" : - "?" - ); - CHEEVOS_LOG("[CHEEVOS]: req_hits: %u\n", cond->req_hits); - CHEEVOS_LOG("[CHEEVOS]: source:\n"); - cheevos_log_var(&cond->source); - CHEEVOS_LOG("[CHEEVOS]: op: %s\n", - cond->op == CHEEVOS_COND_OP_EQUALS ? "==" : - cond->op == CHEEVOS_COND_OP_LESS_THAN ? "<" : - cond->op == CHEEVOS_COND_OP_LESS_THAN_OR_EQUAL ? "<=" : - cond->op == CHEEVOS_COND_OP_GREATER_THAN ? ">" : - cond->op == CHEEVOS_COND_OP_GREATER_THAN_OR_EQUAL ? ">=" : - cond->op == CHEEVOS_COND_OP_NOT_EQUAL_TO ? "!=" : - "?" - ); - CHEEVOS_LOG("[CHEEVOS]: target:\n"); - cheevos_log_var(&cond->target); -} - -static void cheevos_log_cheevo(const cheevo_t* cheevo, - const cheevos_field_t* memaddr_ud) -{ - if (!cheevo || !memaddr_ud) - return; - - CHEEVOS_LOG("[CHEEVOS]: cheevo %p\n", cheevo); - CHEEVOS_LOG("[CHEEVOS]: id: %u\n", cheevo->id); - CHEEVOS_LOG("[CHEEVOS]: title: %s\n", cheevo->title); - CHEEVOS_LOG("[CHEEVOS]: desc: %s\n", cheevo->description); - CHEEVOS_LOG("[CHEEVOS]: author: %s\n", cheevo->author); - CHEEVOS_LOG("[CHEEVOS]: badge: %s\n", cheevo->badge); - CHEEVOS_LOG("[CHEEVOS]: points: %u\n", cheevo->points); - CHEEVOS_LOG("[CHEEVOS]: sets: TBD\n"); - CHEEVOS_LOG("[CHEEVOS]: memaddr: %.*s\n", - (int)memaddr_ud->length, memaddr_ud->string); -} - -static void cheevos_add_var_size(char** aux, size_t* left, - const cheevos_var_t* var) -{ - if (!var) - return; - - switch( var->size ) - { - case CHEEVOS_VAR_SIZE_BIT_0: - cheevos_add_char(aux, left, 'M'); - break; - case CHEEVOS_VAR_SIZE_BIT_1: - cheevos_add_char(aux, left, 'N'); - break; - case CHEEVOS_VAR_SIZE_BIT_2: - cheevos_add_char(aux, left, 'O'); - break; - case CHEEVOS_VAR_SIZE_BIT_3: - cheevos_add_char(aux, left, 'P'); - break; - case CHEEVOS_VAR_SIZE_BIT_4: - cheevos_add_char(aux, left, 'Q'); - break; - case CHEEVOS_VAR_SIZE_BIT_5: - cheevos_add_char(aux, left, 'R'); - break; - case CHEEVOS_VAR_SIZE_BIT_6: - cheevos_add_char(aux, left, 'S'); - break; - case CHEEVOS_VAR_SIZE_BIT_7: - cheevos_add_char(aux, left, 'T'); - break; - case CHEEVOS_VAR_SIZE_NIBBLE_LOWER: - cheevos_add_char(aux, left, 'L'); - break; - case CHEEVOS_VAR_SIZE_NIBBLE_UPPER: - cheevos_add_char(aux, left, 'U'); - break; - case CHEEVOS_VAR_SIZE_EIGHT_BITS: - cheevos_add_char(aux, left, 'H'); - break; - case CHEEVOS_VAR_SIZE_THIRTYTWO_BITS: - cheevos_add_char(aux, left, 'X'); - break; - case CHEEVOS_VAR_SIZE_SIXTEEN_BITS: - default: - cheevos_add_char(aux, left, ' '); - break; - } -} - -static void cheevos_add_var(const cheevos_var_t* var, char** memaddr, - size_t *left) -{ - if (!var) - return; - - if ( var->type == CHEEVOS_VAR_TYPE_ADDRESS - || var->type == CHEEVOS_VAR_TYPE_DELTA_MEM) - { - if (var->type == CHEEVOS_VAR_TYPE_DELTA_MEM) - cheevos_add_char(memaddr, left, 'd'); - else if (var->is_bcd) - cheevos_add_char(memaddr, left, 'b'); - - cheevos_add_string(memaddr, left, "0x"); - cheevos_add_var_size(memaddr, left, var); - cheevos_add_hex(memaddr, left, var->value); - } - else if (var->type == CHEEVOS_VAR_TYPE_VALUE_COMP) - { - cheevos_add_uint(memaddr, left, var->value); - } -} - -static void cheevos_build_memaddr(const cheevos_condition_t* condition, - char* memaddr, size_t left) -{ - size_t i, j; - const cheevos_cond_t* cond; - const cheevos_condset_t *condset; - char *aux = memaddr; - - left--; /* reserve one char for the null terminator */ - - for (i = 0, condset = condition->condsets; - i < condition->count; i++, condset++) - { - if (i != 0) - cheevos_add_char(&aux, &left, 'S'); - - for (j = 0, cond = condset->conds; - j < condset->count; j++, cond++) - { - if (j != 0) - cheevos_add_char(&aux, &left, '_'); - - if (cond->type == CHEEVOS_COND_TYPE_RESET_IF) - cheevos_add_string(&aux, &left, "R:"); - else if (cond->type == CHEEVOS_COND_TYPE_PAUSE_IF) - cheevos_add_string(&aux, &left, "P:"); - else if (cond->type == CHEEVOS_COND_TYPE_ADD_SOURCE) - cheevos_add_string(&aux, &left, "A:"); - else if (cond->type == CHEEVOS_COND_TYPE_SUB_SOURCE) - cheevos_add_string(&aux, &left, "B:"); - else if (cond->type == CHEEVOS_COND_TYPE_ADD_HITS) - cheevos_add_string(&aux, &left, "C:"); - - cheevos_add_var(&cond->source, &aux, &left); - - switch (cond->op) - { - case CHEEVOS_COND_OP_EQUALS: - cheevos_add_char(&aux, &left, '='); - break; - case CHEEVOS_COND_OP_GREATER_THAN: - cheevos_add_char(&aux, &left, '>'); - break; - case CHEEVOS_COND_OP_GREATER_THAN_OR_EQUAL: - cheevos_add_string(&aux, &left, ">="); - break; - case CHEEVOS_COND_OP_LESS_THAN: - cheevos_add_char(&aux, &left, '<'); - break; - case CHEEVOS_COND_OP_LESS_THAN_OR_EQUAL: - cheevos_add_string(&aux, &left, "<="); - break; - case CHEEVOS_COND_OP_NOT_EQUAL_TO: - cheevos_add_string(&aux, &left, "!="); - break; - } - - cheevos_add_var(&cond->target, &aux, &left); - - if (cond->req_hits > 0) - { - cheevos_add_char(&aux, &left, '.'); - cheevos_add_uint(&aux, &left, cond->req_hits); - cheevos_add_char(&aux, &left, '.'); - } - } - } - - *aux = 0; -} - -static void cheevos_post_log_cheevo(const cheevo_t* cheevo) -{ - char memaddr[256]; - if (!cheevo) - return; - cheevos_build_memaddr(&cheevo->condition, memaddr, sizeof(memaddr)); - CHEEVOS_LOG("[CHEEVOS]: memaddr (computed): %s\n", memaddr); -} - -static void cheevos_log_lboard(const cheevos_leaderboard_t* lb) -{ - unsigned i; - char mem[256]; - char* aux = NULL; - size_t left = 0; - - if (!lb) - return; - - CHEEVOS_LOG("[CHEEVOS]: leaderboard %p\n", lb); - CHEEVOS_LOG("[CHEEVOS]: id: %u\n", lb->id); - CHEEVOS_LOG("[CHEEVOS]: title: %s\n", lb->title); - CHEEVOS_LOG("[CHEEVOS]: desc: %s\n", lb->description); - - cheevos_build_memaddr(&lb->start, mem, sizeof(mem)); - CHEEVOS_LOG("[CHEEVOS]: start: %s\n", mem); - - cheevos_build_memaddr(&lb->cancel, mem, sizeof(mem)); - CHEEVOS_LOG("[CHEEVOS]: cancel: %s\n", mem); - - cheevos_build_memaddr(&lb->submit, mem, sizeof(mem)); - CHEEVOS_LOG("[CHEEVOS]: submit: %s\n", mem); - - left = sizeof(mem); - aux = mem; - - for (i = 0; i < lb->value.count; i++) - { - if (i != 0) - cheevos_add_char(&aux, &left, '_'); - - cheevos_add_var(&lb->value.terms[i].var, &aux, &left); - cheevos_add_char(&aux, &left, '*'); - cheevos_add_int(&aux, &left, lb->value.terms[i].multiplier); - } - - CHEEVOS_LOG("[CHEEVOS]: value: %s\n", mem); -} -#endif - static uint32_t cheevos_djb2(const char* str, size_t length) { const unsigned char *aux = (const unsigned char*)str; @@ -1216,17 +896,9 @@ static int cheevos_new_cheevo(cheevos_readud_t *ud) !cheevo->badge) goto error; -#ifdef CHEEVOS_VERBOSE - cheevos_log_cheevo(cheevo, &ud->memaddr); -#endif - if (cheevos_parse_condition(&cheevo->condition, ud->memaddr.string)) goto error; -#ifdef CHEEVOS_VERBOSE - cheevos_post_log_cheevo(cheevo); -#endif - return 0; error: @@ -1348,10 +1020,6 @@ static int cheevos_new_lboard(cheevos_readud_t *ud) if (cheevos_parse_mem(lboard, ud->memaddr.string)) goto error; -#ifdef CHEEVOS_VERBOSE - cheevos_log_lboard(lboard); -#endif - return 0; error: diff --git a/cheevos/cheevos.h b/cheevos/cheevos.h index 0bcb4894f7..a4f18c7c7f 100644 --- a/cheevos/cheevos.h +++ b/cheevos/cheevos.h @@ -45,13 +45,10 @@ End of setup #else -static void STUB_LOG(const char *fmt, ...) -{ - (void)fmt; -} +void cheevos_log(const char *fmt, ...); -#define CHEEVOS_LOG STUB_LOG -#define CHEEVOS_ERR STUB_LOG +#define CHEEVOS_LOG cheevos_log +#define CHEEVOS_ERR cheevos_log #endif diff --git a/cheevos/cond.c b/cheevos/cond.c index c9b54b619b..9f665900c9 100644 --- a/cheevos/cond.c +++ b/cheevos/cond.c @@ -168,9 +168,6 @@ void cheevos_cond_parse_in_set(cheevos_cond_t* cond, const char* memaddr, unsign if (index == which) { cheevos_cond_parse(cond, &memaddr); -#ifdef CHEEVOS_VERBOSE - /*cheevos_log_cond(cond);*/ -#endif cond++; } else From 09394198b238cf7682a8249f4636993b55efad6e Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Wed, 25 Apr 2018 22:10:04 +0200 Subject: [PATCH 485/517] Update CHANGES.md --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 6a84b56d14..85404e6ac3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -45,6 +45,13 @@ that support it (so far this includes - D3D8/D3D9, OpenGL, Vulkan) - IOS: Fixed crash when opening downloaded roms from Safari or using the "Open in.." functionality. Added the compiler flag to support keyboard remapping to controls. - IOS: Fixed buffer overlap that caused a crash while trying to download GLSL shaders from the buildbot. - PS3: fix URLS +- REMAPS: Mapping keyboard keys from more than one gamepad (works with dosbox) +- REMAPS: Mapping more than one button to the same action +- REMAPS: Unmapping buttons +- REMAPS: Unmapping analogs +- REMAPS: Mapping a button to trigger an analog response (tested with mupen, can run on SM64 with the d-pad now, triggers a full analog tilt) +- REMAPS: Mapping an analog to another analog (having more than one analog mapped to the same output causes issues) +- REMAPS: Mapping an analog to produce a button response - SCANNER: Should be able to scan dual-layer Wii disc images now, filestream code now supports files larger than 4GB. - SHADERS/SLANG: Slang shaders should work again on Android version and MSVC versions (basically all the Griffin-based versions). - SHADERS: If GL context is GLES2/3/Core context, Cg shaders are unavailable. Applies to shader list too. From 9663f5199ea7a2b6efc754d8a48fd2c097100fe8 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 25 Apr 2018 22:20:48 +0200 Subject: [PATCH 486/517] (Switch) Enable HAVE_RUNAHEAD for Switch --- Makefile.switch | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.switch b/Makefile.switch index a477749409..d81bd05b3c 100644 --- a/Makefile.switch +++ b/Makefile.switch @@ -12,6 +12,7 @@ ifeq ($(GRIFFIN_BUILD), 1) OBJ += griffin/griffin.o DEFINES += -DHAVE_GRIFFIN=1 -DHAVE_NEON -DHAVE_MATERIALUI -DHAVE_LIBRETRODB -DHAVE_CC_RESAMPLER DEFINES += -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DWANT_ZLIB + DEFINES += -DHAVE_RUNAHEAD else HAVE_CC_RESAMPLER = 1 HAVE_MENU_COMMON = 1 @@ -29,6 +30,7 @@ else HAVE_STATIC_VIDEO_FILTERS = 1 HAVE_STATIC_AUDIO_FILTERS = 1 HAVE_MENU = 1 + HAVE_RUNAHEAD = 1 include Makefile.common BLACKLIST := From 0964444e06b767de24dfe70d75ac180f9074a7fd Mon Sep 17 00:00:00 2001 From: radius Date: Wed, 25 Apr 2018 19:50:51 -0500 Subject: [PATCH 487/517] prevent crashing in cores that don't range check retro_set_controller_port --- command.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command.c b/command.c index 64b4f9f5f4..1a279788d8 100644 --- a/command.c +++ b/command.c @@ -1015,7 +1015,7 @@ static void command_event_init_controllers(void) /* Ideally these checks shouldn't be required but if we always * call core_set_controller_port_device input won't work on * cores that don't set port information properly */ - if (info && info->ports.size != 0 && i < info->ports.size) + if (info && info->ports.size != 0) set_controller = true; break; default: @@ -1029,7 +1029,7 @@ static void command_event_init_controllers(void) break; } - if (set_controller) + if (set_controller && i < info->ports.size) { pad.device = device; pad.port = i; From f378e2bfcfe090f12e23397ec6875b79e4de307e Mon Sep 17 00:00:00 2001 From: gblues Date: Wed, 25 Apr 2018 21:28:33 -0700 Subject: [PATCH 488/517] Fix WaveBird support for the Wii U GCA == DETAILS Thanks to JacobM at GBAtemp for helping me test this. The WaveBird wasn't being properly picked up due to the port status byte being different from normal GC controllers. (Why? who knows. Probably so games could detect the WB and show WB-specific OSDs). This implementation should be more future-proof, to handle any other unexpected status bytes. --- input/common/hid/device_wiiu_gca.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c index 1a26800041..73e58681fb 100644 --- a/input/common/hid/device_wiiu_gca.c +++ b/input/common/hid/device_wiiu_gca.c @@ -22,10 +22,10 @@ static uint8_t activation_packet[] = { 0x01, 0x13 }; static uint8_t activation_packet[] = { 0x13 }; #endif -#define GCA_PORT_INITIALIZING 0x00 -#define GCA_PORT_POWERED 0x04 -#define GCA_PORT_CONNECTED 0x10 - +#define GCA_PORT_INITIALIZING 0x00 +#define GCA_PORT_POWERED 0x04 +#define GCA_PORT_CONNECTED 0x10 +#define GCA_WAVEBIRD_CONNECTED 0x22 typedef struct wiiu_gca_instance { void *handle; @@ -116,15 +116,14 @@ static void update_pad_state(wiiu_gca_instance_t *instance) joypad_connection_t *pad; /* process each pad */ - //RARCH_LOG_BUFFER(instance->device_state, 37); for(i = 1; i < 37; i += 9) { port = i / 9; pad = instance->pads[port]; - port_connected = instance->device_state[i] & GCA_PORT_CONNECTED; + port_connected = instance->device_state[i]; - if(port_connected) + if(port_connected > GCA_PORT_POWERED) { if(pad == NULL) { @@ -328,9 +327,9 @@ static int16_t wiiu_gca_get_axis(void *data, unsigned axis) static const char *wiiu_gca_get_name(void *data) { - gca_pad_t *pad = (gca_pad_t *)data; + gca_pad_t *pad = (gca_pad_t *)data; - return "GameCube Controller"; + return "GameCube Controller"; } /** From c482aff8b288881dfb689e5e90f5544c55185d1d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 26 Apr 2018 13:38:27 +0200 Subject: [PATCH 489/517] No idea where this character came from --- verbosity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verbosity.c b/verbosity.c index 4fc7686f50..6102669a78 100644 --- a/verbosity.c +++ b/verbosity.c @@ -125,7 +125,7 @@ void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap) #if TARGET_IPHONE_SIMULATOR vprintf(fmt, ap); #else - static aslclient asl_client;ß + static aslclient asl_client; static int asl_initialized = 0; if (!asl_initialized) { From c6d6bb26c359f9230922ceb9c553d5c22e895da6 Mon Sep 17 00:00:00 2001 From: Matt Sephton Date: Thu, 26 Apr 2018 13:54:23 +0100 Subject: [PATCH 490/517] changed APP_UNIQUE_ID to next free & valid HEX number was 0xBAC1G --- pkg/ctr/Makefile.cores | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/ctr/Makefile.cores b/pkg/ctr/Makefile.cores index 5fad8c802d..8fabdb8bd7 100644 --- a/pkg/ctr/Makefile.cores +++ b/pkg/ctr/Makefile.cores @@ -75,7 +75,7 @@ else ifeq ($(LIBRETRO), freeintv) APP_TITLE = FreeIntv APP_AUTHOR = various APP_PRODUCT_CODE = RARCH-FREEINTV - APP_UNIQUE_ID = 0xBAC1G + APP_UNIQUE_ID = 0xBAC21 APP_ICON = pkg/ctr/assets/freeintv.png APP_BANNER = pkg/ctr/assets/freeintv_banner.png From 37b1c9e8e9af407485fdad601c3c5410f82e5ed7 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 26 Apr 2018 16:05:57 +0200 Subject: [PATCH 491/517] Add NULL entry --- gfx/drivers/gl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 26335bd7d2..200d9d6343 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -1687,7 +1687,8 @@ static void gl_begin_debug(gl_t *gl) extern gl_renderchain_driver_t gl2_renderchain; static const gl_renderchain_driver_t *renderchain_gl_drivers[] = { - &gl2_renderchain + &gl2_renderchain, + NULL }; static bool renderchain_gl_init_first( From f1d707a043e052e9570ac338bdd534e831d45f1c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 26 Apr 2018 20:45:01 +0200 Subject: [PATCH 492/517] Running gl_check_capability from gl_get_flags resulted in a crash on some Android systems, so unfortunately we cannot selectively hide away GPU Hard Sync for now --- gfx/drivers/gl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 200d9d6343..34b781fad3 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2605,9 +2605,7 @@ static uint32_t gl_get_flags(void *data) { uint32_t flags = 0; - if (gl_check_capability(GL_CAPS_SYNC)) - BIT32_SET(flags, GFX_CTX_FLAGS_HARD_SYNC); - + BIT32_SET(flags, GFX_CTX_FLAGS_HARD_SYNC); BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING); From 80025692c18553f528b8abfb2c0e472d82ff443c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 26 Apr 2018 21:25:38 +0200 Subject: [PATCH 493/517] (Android) Bump up version code --- pkg/android/phoenix/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/android/phoenix/AndroidManifest.xml b/pkg/android/phoenix/AndroidManifest.xml index cefb6e34a1..9671cc6bbf 100644 --- a/pkg/android/phoenix/AndroidManifest.xml +++ b/pkg/android/phoenix/AndroidManifest.xml @@ -1,7 +1,7 @@ From 76b4b426af189dc50a6a0432aeb25ee786169f6c Mon Sep 17 00:00:00 2001 From: orbea Date: Fri, 27 Apr 2018 09:54:05 -0700 Subject: [PATCH 494/517] Makefile.common: Fix segfaults with --disable-builtinflac. --- Makefile.common | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.common b/Makefile.common index e5924dd99f..7bde9ea982 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1463,7 +1463,8 @@ ifeq ($(HAVE_BUILTINFLAC),1) endif OBJ += $(FLACOBJ) else ifeq ($(HAVE_FLAC),1) - LIBS += $(FLAC_LIBS) + DEFINES += -DHAVE_FLAC + LIBS += $(FLAC_LIBS) endif ifeq ($(HAVE_ZLIB), 1) From 0e013ced7557f0d77b835e9558dd88d52d7b288c Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Fri, 27 Apr 2018 12:42:46 -0500 Subject: [PATCH 495/517] Fix typo in mali fbdev driver. --- gfx/drivers_context/mali_fbdev_ctx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gfx/drivers_context/mali_fbdev_ctx.c b/gfx/drivers_context/mali_fbdev_ctx.c index b2ef7917c8..8e88fbbcc7 100644 --- a/gfx/drivers_context/mali_fbdev_ctx.c +++ b/gfx/drivers_context/mali_fbdev_ctx.c @@ -179,8 +179,7 @@ static bool gfx_ctx_mali_fbdev_set_video_mode(void *data, mali->native_window.width = vinfo.xres; mali->native_window.height = vinfo.yres; - mali->refresh_rate = - ctx->refresh_rate = 1000000.0f / vinfo.pixclock * 1000000.0f / + mali->refresh_rate = 1000000.0f / vinfo.pixclock * 1000000.0f / (vinfo.yres + vinfo.upper_margin + vinfo.lower_margin + vinfo.vsync_len) (vinfo.xres + vinfo.left_margin + vinfo.right_margin + vinfo.hsync_len); From c7f04102c719b95533b0bec821655aceee79a554 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Fri, 27 Apr 2018 12:48:58 -0500 Subject: [PATCH 496/517] Adjust travis build to only use libraries the build-server supports. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c05e82b7bf..5fa1900042 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ matrix: - g++-mingw-w64-i686 - mingw-w64-i686-dev script: - - CROSS_COMPILE=i686-w64-mingw32- ./configure --disable-d3d8 --disable-d3d9 --disable-d3d10 --disable-d3d11 --disable-d3d12 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1 + - CROSS_COMPILE=i686-w64-mingw32- CFLAGS="-D_WIN32_WINNT=0x0501" ./configure --disable-d3d8 --disable-d3d9 --disable-d3d10 --disable-d3d11 --disable-d3d12 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1 - compiler: mingw-x64 addons: apt: @@ -18,7 +18,7 @@ matrix: - g++-mingw-w64-x86-64 - mingw-w64-x86-64-dev script: - - CROSS_COMPILE=x86_64-w64-mingw32- ./configure --disable-d3d8 --disable-d3d9 --disable-d3d10 --disable-d3d11 --disable-d3d12 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1 + - CROSS_COMPILE=x86_64-w64-mingw32- CFLAGS="-D_WIN32_WINNT=0x0501" ./configure --disable-d3d8 --disable-d3d9 --disable-d3d10 --disable-d3d11 --disable-d3d12 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1 - compiler: gcc - compiler: clang addons: From 1f2ef858ab579fa88d6e368e88a230dfb0de1025 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 27 Apr 2018 23:12:24 +0200 Subject: [PATCH 497/517] Update libretro-common --- libretro-common/audio/audio_mixer.c | 199 +++++++++++++++++++- libretro-common/file/file_path.c | 2 +- libretro-common/include/audio/audio_mixer.h | 4 +- 3 files changed, 199 insertions(+), 6 deletions(-) diff --git a/libretro-common/audio/audio_mixer.c b/libretro-common/audio/audio_mixer.c index 7988ff6517..b5bc82d906 100644 --- a/libretro-common/audio/audio_mixer.c +++ b/libretro-common/audio/audio_mixer.c @@ -47,12 +47,17 @@ #include #endif +#ifdef HAVE_DR_FLAC +#define DR_FLAC_IMPLEMENTATION +#include +#endif + #ifdef HAVE_IBXM #include #endif #define AUDIO_MIXER_MAX_VOICES 8 -#define AUDIO_MIXER_TEMP_OGG_BUFFER 8192 +#define AUDIO_MIXER_TEMP_BUFFER 8192 struct audio_mixer_sound { @@ -75,6 +80,15 @@ struct audio_mixer_sound const void* data; } ogg; #endif + +#ifdef HAVE_DR_FLAC + struct + { + /* flac */ + unsigned size; + const void* data; + } flac; +#endif #ifdef HAVE_IBXM struct @@ -116,6 +130,20 @@ struct audio_mixer_voice } ogg; #endif +#ifdef HAVE_DR_FLAC + struct + { + unsigned position; + unsigned samples; + unsigned buf_samples; + float* buffer; + float ratio; + drflac *stream; + void *resampler_data; + const retro_resampler_t *resampler; + } flac; +#endif + #ifdef HAVE_IBXM struct { @@ -343,6 +371,25 @@ audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size) #endif } + +audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size) +{ +#ifdef HAVE_DR_FLAC + audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound)); + + if (!sound) + return NULL; + + sound->type = AUDIO_MIXER_TYPE_FLAC; + sound->types.flac.size = size; + sound->types.flac.data = buffer; + + return sound; +#else + return NULL; +#endif +} + audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size) { #ifdef HAVE_IBXM @@ -386,6 +433,13 @@ void audio_mixer_destroy(audio_mixer_sound_t* sound) handle = (void*)sound->types.mod.data; if (handle) free(handle); +#endif + break; + case AUDIO_MIXER_TYPE_FLAC: +#ifdef HAVE_DR_FLAC + handle = (void*)sound->types.flac.data; + if (handle) + free(handle); #endif break; case AUDIO_MIXER_TYPE_NONE: @@ -436,7 +490,7 @@ static bool audio_mixer_play_ogg( goto error; } - samples = (unsigned)(AUDIO_MIXER_TEMP_OGG_BUFFER * ratio); + samples = (unsigned)(AUDIO_MIXER_TEMP_BUFFER * ratio); ogg_buffer = (float*)memalign_alloc(16, ((samples + 15) & ~15) * sizeof(float)); @@ -531,6 +585,61 @@ error: } #endif +#ifdef HAV_DR_FLAC +static bool audio_mixer_play_flac( + audio_mixer_sound_t* sound, + audio_mixer_voice_t* voice, + bool repeat, float volume, + audio_mixer_stop_cb_t stop_cb) +{ + int res = 0; + float ratio = 1.0f; + unsigned samples = 0; + void *flac_buffer = NULL; + void *resampler_data = NULL; + const retro_resampler_t* resamp = NULL; + drflac *dr_flac = drflac_open_memory((const unsigned char*)sound->types.flac.data,sound->types.flac.size); + + + if (!dr_flac) + return false; + if (dr_flac->sampleRate != s_rate) + { + ratio = (double)s_rate / (double)(dr_flac->sampleRate); + + if (!retro_resampler_realloc(&resampler_data, + &resamp, NULL, RESAMPLER_QUALITY_DONTCARE, + ratio)) + goto error; + } + + samples = (unsigned)(AUDIO_MIXER_TEMP_BUFFER * ratio); + flac_buffer = (float*)memalign_alloc(16, + ((samples + 15) & ~15) * sizeof(float)); + + if (!flac_buffer) + { + resamp->free(resampler_data); + goto error; + } + + voice->types.flac.resampler = resamp; + voice->types.flac.resampler_data = resampler_data; + voice->types.flac.buffer = (float*)flac_buffer; + voice->types.flac.buf_samples = samples; + voice->types.flac.ratio = ratio; + voice->types.flac.stream = dr_flac; + voice->types.flac.position = 0; + voice->types.flac.samples = 0; + + return true; + +error: + drflac_close(dr_flac); + return false; +} +#endif + audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound, bool repeat, float volume, audio_mixer_stop_cb_t stop_cb) { @@ -563,6 +672,11 @@ audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound, bool repeat, case AUDIO_MIXER_TYPE_MOD: #ifdef HAVE_IBXM res = audio_mixer_play_mod(sound, voice, repeat, volume, stop_cb); +#endif + break; + case AUDIO_MIXER_TYPE_FLAC: +#ifdef HAVE_DR_FLAC + res = audio_mixer_play_flac(sound, voice, repeat, volume, stop_cb); #endif break; case AUDIO_MIXER_TYPE_NONE: @@ -666,7 +780,7 @@ static void audio_mixer_mix_ogg(float* buffer, size_t num_frames, { int i; struct resampler_data info = { 0 }; - float temp_buffer[AUDIO_MIXER_TEMP_OGG_BUFFER] = { 0 }; + float temp_buffer[AUDIO_MIXER_TEMP_BUFFER] = { 0 }; unsigned buf_free = (unsigned)(num_frames * 2); unsigned temp_samples = 0; float* pcm = NULL; @@ -676,7 +790,7 @@ static void audio_mixer_mix_ogg(float* buffer, size_t num_frames, again: temp_samples = stb_vorbis_get_samples_float_interleaved( voice->types.ogg.stream, 2, temp_buffer, - AUDIO_MIXER_TEMP_OGG_BUFFER) * 2; + AUDIO_MIXER_TEMP_BUFFER) * 2; if (temp_samples == 0) { @@ -809,6 +923,78 @@ again: } #endif +#ifdef HAVE_DR_FLAC +static void audio_mixer_mix_flac(float* buffer, size_t num_frames, + audio_mixer_voice_t* voice, + float volume) +{ + int i; + struct resampler_data info = { 0 }; + float temp_buffer[AUDIO_MIXER_TEMP_BUFFER] = { 0 }; + unsigned buf_free = (unsigned)(num_frames * 2); + unsigned temp_samples = 0; + float* pcm = NULL; + + if (voice->types.flac.position == voice->types.flac.samples) + { +again: + temp_samples = drflac_read_f32( voice->types.flac.stream, AUDIO_MIXER_TEMP_BUFFER, temp_buffer); + if (temp_samples == 0) + { + if (voice->repeat) + { + if (voice->stop_cb) + voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED); + + drflac_seek_to_sample(voice->types.flac.stream,0); + goto again; + } + else + { + if (voice->stop_cb) + voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED); + + voice->type = AUDIO_MIXER_TYPE_NONE; + return; + } + } + + info.data_in = temp_buffer; + info.data_out = voice->types.flac.buffer; + info.input_frames = temp_samples / 2; + info.output_frames = 0; + info.ratio = voice->types.flac.ratio; + + if (voice->types.flac.resampler) + voice->types.ogg.resampler->process(voice->types.flac.resampler_data, &info); + else + memcpy(voice->types.flac.buffer, temp_buffer, temp_samples * sizeof(float)); + voice->types.flac.position = 0; + voice->types.flac.samples = voice->types.flac.buf_samples; + } + + pcm = voice->types.flac.buffer + voice->types.flac.position; + + if (voice->types.flac.samples < buf_free) + { + for (i = voice->types.flac.samples; i != 0; i--) + *buffer++ += *pcm++ * volume; + + buf_free -= voice->types.flac.samples; + goto again; + } + else + { + int i; + for (i = buf_free; i != 0; --i ) + *buffer++ += *pcm++ * volume; + + voice->types.flac.position += buf_free; + voice->types.flac.samples -= buf_free; + } +} +#endif + void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bool override) { unsigned i; @@ -837,6 +1023,11 @@ void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bo case AUDIO_MIXER_TYPE_MOD: #ifdef HAVE_IBXM audio_mixer_mix_mod(buffer, num_frames, voice, volume); +#endif + break; + case AUDIO_MIXER_TYPE_FLAC: +#ifdef HAVE_DR_FLAC + audio_mixer_mix_flac(buffer, num_frames, voice, volume); #endif break; case AUDIO_MIXER_TYPE_NONE: diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index 0b4c4b1244..7c79b0b44f 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -392,7 +392,7 @@ char *path_remove_extension(char *path) return NULL; if (*last) *last = '\0'; - return last; + return path; } /** diff --git a/libretro-common/include/audio/audio_mixer.h b/libretro-common/include/audio/audio_mixer.h index ec319bc620..fa21b02e21 100644 --- a/libretro-common/include/audio/audio_mixer.h +++ b/libretro-common/include/audio/audio_mixer.h @@ -41,7 +41,8 @@ enum audio_mixer_type AUDIO_MIXER_TYPE_NONE = 0, AUDIO_MIXER_TYPE_WAV, AUDIO_MIXER_TYPE_OGG, - AUDIO_MIXER_TYPE_MOD + AUDIO_MIXER_TYPE_MOD, + AUDIO_MIXER_TYPE_FLAC }; typedef struct audio_mixer_sound audio_mixer_sound_t; @@ -61,6 +62,7 @@ void audio_mixer_done(void); audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size); audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size); audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size); +audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size); void audio_mixer_destroy(audio_mixer_sound_t* sound); From 958602d3288411c31db297584ffb1631104e210f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 27 Apr 2018 23:17:28 +0200 Subject: [PATCH 498/517] HAVE_BUILTINFLAC no longer needs to be excluded for C89 --- deps/libFLAC/cpu.c | 2 +- deps/libFLAC/include/FLAC/format.h | 2 +- deps/libFLAC/include/share/alloc.h | 30 ++++++++++++++++-------------- qb/config.params.sh | 1 - 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/deps/libFLAC/cpu.c b/deps/libFLAC/cpu.c index 3f0a2346db..dc25863641 100644 --- a/deps/libFLAC/cpu.c +++ b/deps/libFLAC/cpu.c @@ -83,7 +83,7 @@ cpu_xgetbv_x86(void) return (uint32_t)_xgetbv(0); #elif defined __GNUC__ uint32_t lo, hi; - asm volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0)); + __asm__ volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0)); return lo; #else return 0; diff --git a/deps/libFLAC/include/FLAC/format.h b/deps/libFLAC/include/FLAC/format.h index c087d4a70e..99ed7c5116 100644 --- a/deps/libFLAC/include/FLAC/format.h +++ b/deps/libFLAC/include/FLAC/format.h @@ -512,7 +512,7 @@ typedef enum { FLAC__METADATA_TYPE_UNDEFINED = 7, /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */ - FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE, + FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE /**< No type will ever be greater than this. There is not enough room in the protocol block. */ } FLAC__MetadataType; diff --git a/deps/libFLAC/include/share/alloc.h b/deps/libFLAC/include/share/alloc.h index 914de9ba6b..19349d2a75 100644 --- a/deps/libFLAC/include/share/alloc.h +++ b/deps/libFLAC/include/share/alloc.h @@ -33,6 +33,8 @@ #ifndef FLAC__SHARE__ALLOC_H #define FLAC__SHARE__ALLOC_H +#include + #ifdef HAVE_CONFIG_H # include #endif @@ -66,7 +68,7 @@ /* avoid malloc()ing 0 bytes, see: * https://www.securecoding.cert.org/confluence/display/seccode/MEM04-A.+Do+not+make+assumptions+about+the+result+of+allocating+0+bytes?focusedCommentId=5407003 */ -static inline void *safe_malloc_(size_t size) +static INLINE void *safe_malloc_(size_t size) { /* malloc(0) is undefined; FLAC src convention is to always allocate */ if(!size) @@ -74,7 +76,7 @@ static inline void *safe_malloc_(size_t size) return malloc(size); } -static inline void *safe_calloc_(size_t nmemb, size_t size) +static INLINE void *safe_calloc_(size_t nmemb, size_t size) { if(!nmemb || !size) return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ @@ -83,7 +85,7 @@ static inline void *safe_calloc_(size_t nmemb, size_t size) /*@@@@ there's probably a better way to prevent overflows when allocating untrusted sums but this works for now */ -static inline void *safe_malloc_add_2op_(size_t size1, size_t size2) +static INLINE void *safe_malloc_add_2op_(size_t size1, size_t size2) { size2 += size1; if(size2 < size1) @@ -91,7 +93,7 @@ static inline void *safe_malloc_add_2op_(size_t size1, size_t size2) return safe_malloc_(size2); } -static inline void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3) +static INLINE void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3) { size2 += size1; if(size2 < size1) @@ -102,7 +104,7 @@ static inline void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size return safe_malloc_(size3); } -static inline void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4) +static INLINE void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4) { size2 += size1; if(size2 < size1) @@ -118,7 +120,7 @@ static inline void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size void *safe_malloc_mul_2op_(size_t size1, size_t size2) ; -static inline void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3) +static INLINE void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3) { if(!size1 || !size2 || !size3) return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ @@ -131,7 +133,7 @@ static inline void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size } /* size1*size2 + size3 */ -static inline void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3) +static INLINE void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3) { if(!size1 || !size2) return safe_malloc_(size3); @@ -141,7 +143,7 @@ static inline void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size } /* size1 * (size2 + size3) */ -static inline void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3) +static INLINE void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3) { if(!size1 || (!size2 && !size3)) return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ @@ -153,7 +155,7 @@ static inline void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size return malloc(size1*size2); } -static inline void *safe_realloc_(void *ptr, size_t size) +static INLINE void *safe_realloc_(void *ptr, size_t size) { void *oldptr = ptr; void *newptr = realloc(ptr, size); @@ -161,7 +163,7 @@ static inline void *safe_realloc_(void *ptr, size_t size) free(oldptr); return newptr; } -static inline void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) +static INLINE void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) { size2 += size1; if(size2 < size1) { @@ -171,7 +173,7 @@ static inline void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) return realloc(ptr, size2); } -static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) +static INLINE void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) { size2 += size1; if(size2 < size1) @@ -182,7 +184,7 @@ static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, return realloc(ptr, size3); } -static inline void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) +static INLINE void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) { size2 += size1; if(size2 < size1) @@ -196,7 +198,7 @@ static inline void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, return realloc(ptr, size4); } -static inline void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) +static INLINE void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) { if(!size1 || !size2) return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ @@ -206,7 +208,7 @@ static inline void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) } /* size1 * (size2 + size3) */ -static inline void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) +static INLINE void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) { if(!size1 || (!size2 && !size3)) return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ diff --git a/qb/config.params.sh b/qb/config.params.sh index cc23750792..126744aabe 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -96,7 +96,6 @@ HAVE_CHD=yes # Compile in chd support HAVE_7ZIP=yes # Compile in 7z support HAVE_FLAC=auto # Compile in flac support HAVE_BUILTINFLAC=yes # Bake in flac support -C89_BUILTINFLAC=no HAVE_UPDATE_ASSETS=yes # Disable downloading assets with online updater HAVE_PRESERVE_DYLIB=no # Enable dlclose() for Valgrind support HAVE_PARPORT=auto # Parallel port joypad support From 714f7b7b135d4d8f7ec97495255162c4b239a469 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 27 Apr 2018 23:21:44 +0200 Subject: [PATCH 499/517] Cleanups --- gfx/common/dxgi_common.h | 2 +- gfx/drivers_shader/glslang_util.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gfx/common/dxgi_common.h b/gfx/common/dxgi_common.h index a35090dfb5..32af9b89b2 100644 --- a/gfx/common/dxgi_common.h +++ b/gfx/common/dxgi_common.h @@ -767,7 +767,7 @@ static INLINE HRESULT DXGICreateFactory(DXGIFactory* factory) #define DXGI_COLOR_RGBA(r, g, b, a) (((UINT32)(a) << 24) | ((UINT32)(b) << 16) | ((UINT32)(g) << 8) | ((UINT32)(r) << 0)) typedef enum { - DXGI_FORMAT_EX_A4R4G4B4_UNORM = 1000, + DXGI_FORMAT_EX_A4R4G4B4_UNORM = 1000 } DXGI_FORMAT_EX; typedef struct diff --git a/gfx/drivers_shader/glslang_util.h b/gfx/drivers_shader/glslang_util.h index 006fadc490..cd26e42380 100644 --- a/gfx/drivers_shader/glslang_util.h +++ b/gfx/drivers_shader/glslang_util.h @@ -23,7 +23,7 @@ typedef enum glslang_format { SLANG_FORMAT_UNKNOWN = 0, - // 8-bit + /* 8-bit */ SLANG_FORMAT_R8_UNORM, SLANG_FORMAT_R8_UINT, SLANG_FORMAT_R8_SINT, @@ -35,11 +35,11 @@ typedef enum glslang_format SLANG_FORMAT_R8G8B8A8_SINT, SLANG_FORMAT_R8G8B8A8_SRGB, - // 10-bit + /* 10-bit */ SLANG_FORMAT_A2B10G10R10_UNORM_PACK32, SLANG_FORMAT_A2B10G10R10_UINT_PACK32, - // 16-bit + /* 16-bit */ SLANG_FORMAT_R16_UINT, SLANG_FORMAT_R16_SINT, SLANG_FORMAT_R16_SFLOAT, @@ -50,7 +50,7 @@ typedef enum glslang_format SLANG_FORMAT_R16G16B16A16_SINT, SLANG_FORMAT_R16G16B16A16_SFLOAT, - // 32-bit + /* 32-bit */ SLANG_FORMAT_R32_UINT, SLANG_FORMAT_R32_SINT, SLANG_FORMAT_R32_SFLOAT, @@ -105,7 +105,7 @@ struct glslang_output bool glslang_compile_shader(const char *shader_path, glslang_output *output); -// Helpers for internal use. +/* Helpers for internal use. */ bool glslang_read_shader_file(const char *path, std::vector *output, bool root_file); bool glslang_parse_meta(const std::vector &lines, glslang_meta *meta); #endif From faa99bd3230270c1392df206e26ff2d5ffbf288c Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 27 Apr 2018 23:45:06 +0200 Subject: [PATCH 500/517] Start adding FLAC hooks --- Makefile.common | 4 + audio/audio_driver.c | 8 + deps/dr/dr_flac.h | 5437 +++++++++++++++++++ libretro-common/audio/audio_mixer.c | 2 +- libretro-common/include/audio/audio_mixer.h | 1 + 5 files changed, 5451 insertions(+), 1 deletion(-) create mode 100644 deps/dr/dr_flac.h diff --git a/Makefile.common b/Makefile.common index 7bde9ea982..59dd0dc7fd 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1437,6 +1437,10 @@ endif ifeq ($(HAVE_BUILTINFLAC),1) HAVE_FLAC = 1 +ifneq ($(C89_BUILD), 1) + DEFINES += -DHAVE_DR_FLAC -I$(DEPS_DIR) + CFLAGS += -DHAVE_DR_FLAC +endif CFLAGS += -DHAVE_FLAC -I$(DEPS_DIR)/libFLAC/include DEFINES += -DHAVE_STDINT_H -DHAVE_LROUND -DFLAC__HAS_OGG=0 \ -DFLAC_PACKAGE_VERSION="\"retroarch\"" diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 4ef2d3ce54..bd810910a6 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -976,6 +976,9 @@ bool audio_driver_mixer_extension_supported(const char *ext) string_list_append(str_list, "mod", attr); string_list_append(str_list, "s3m", attr); string_list_append(str_list, "xm", attr); +#endif +#ifdef HAVE_DR_FLAC + string_list_append(str_list, "flac", attr); #endif string_list_append(str_list, "wav", attr); @@ -1074,6 +1077,11 @@ bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params) case AUDIO_MIXER_TYPE_MOD: handle = audio_mixer_load_mod(buf, (int32_t)params->bufsize); break; + case AUDIO_MIXER_TYPE_FLAC: +#ifdef HAVE_DR_FLAC + handle = audio_mixer_load_flac(buf, (int32_t)params->bufsize); +#endif + break; case AUDIO_MIXER_TYPE_NONE: free(buf); return false; diff --git a/deps/dr/dr_flac.h b/deps/dr/dr_flac.h new file mode 100644 index 0000000000..4789d1a022 --- /dev/null +++ b/deps/dr/dr_flac.h @@ -0,0 +1,5437 @@ +#ifndef dr_flac_h +#define dr_flac_h + +#include + +#if defined(_MSC_VER) && _MSC_VER < 1600 +typedef signed char drflac_int8; +typedef unsigned char drflac_uint8; +typedef signed short drflac_int16; +typedef unsigned short drflac_uint16; +typedef signed int drflac_int32; +typedef unsigned int drflac_uint32; +typedef signed __int64 drflac_int64; +typedef unsigned __int64 drflac_uint64; +#else +#include +typedef int8_t drflac_int8; +typedef uint8_t drflac_uint8; +typedef int16_t drflac_int16; +typedef uint16_t drflac_uint16; +typedef int32_t drflac_int32; +typedef uint32_t drflac_uint32; +typedef int64_t drflac_int64; +typedef uint64_t drflac_uint64; +#endif +typedef drflac_uint8 drflac_bool8; +typedef drflac_uint32 drflac_bool32; +#define DRFLAC_TRUE 1 +#define DRFLAC_FALSE 0 + +/* As data is read from the client it is placed into an internal buffer for fast access. This controls the + * size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing + * returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8. + */ +#ifndef DR_FLAC_BUFFER_SIZE +#define DR_FLAC_BUFFER_SIZE 4096 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Check if we can enable 64-bit optimizations. */ +#if defined(_WIN64) +#define DRFLAC_64BIT +#endif + +#if defined(__GNUC__) +#if defined(__x86_64__) || defined(__ppc64__) +#define DRFLAC_64BIT +#endif +#endif + +#ifdef DRFLAC_64BIT +typedef drflac_uint64 drflac_cache_t; +#else +typedef drflac_uint32 drflac_cache_t; +#endif + +/* The various metadata block types. */ +#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 +#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 +#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 +#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 +#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 +#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 +#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 +#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 + +/* The various picture types specified in the PICTURE block. */ +#define DRFLAC_PICTURE_TYPE_OTHER 0 +#define DRFLAC_PICTURE_TYPE_FILE_ICON 1 +#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 +#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 +#define DRFLAC_PICTURE_TYPE_COVER_BACK 4 +#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 +#define DRFLAC_PICTURE_TYPE_MEDIA 6 +#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 +#define DRFLAC_PICTURE_TYPE_ARTIST 8 +#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 +#define DRFLAC_PICTURE_TYPE_BAND 10 +#define DRFLAC_PICTURE_TYPE_COMPOSER 11 +#define DRFLAC_PICTURE_TYPE_LYRICIST 12 +#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 +#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 +#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 +#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 +#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 +#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 +#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 +#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 + +typedef enum +{ + drflac_container_native, + drflac_container_ogg, + drflac_container_unknown +} drflac_container; + +typedef enum +{ + drflac_seek_origin_start, + drflac_seek_origin_current +} drflac_seek_origin; + +/* Packing is important on this structure because we map this directly to the raw data within the SEEKTABLE metadata block. */ +#pragma pack(2) +typedef struct +{ + drflac_uint64 firstSample; + drflac_uint64 frameOffset; /* The offset from the first byte of the header of the first frame. */ + drflac_uint16 sampleCount; +} drflac_seekpoint; +#pragma pack() + +typedef struct +{ + drflac_uint16 minBlockSize; + drflac_uint16 maxBlockSize; + drflac_uint32 minFrameSize; + drflac_uint32 maxFrameSize; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalSampleCount; + drflac_uint8 md5[16]; +} drflac_streaminfo; + +typedef struct +{ + /* The metadata type. Use this to know how to interpret the data below. */ + drflac_uint32 type; + + /* A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to + * not modify the contents of this buffer. Use the structures below for more meaningful and structured + * information about the metadata. It's possible for this to be null. + */ + const void* pRawData; + + /* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */ + drflac_uint32 rawDataSize; + + union + { + drflac_streaminfo streaminfo; + + struct + { + int unused; + } padding; + + struct + { + drflac_uint32 id; + const void* pData; + drflac_uint32 dataSize; + } application; + + struct + { + drflac_uint32 seekpointCount; + const drflac_seekpoint* pSeekpoints; + } seektable; + + struct + { + drflac_uint32 vendorLength; + const char* vendor; + drflac_uint32 commentCount; + const char* comments; + } vorbis_comment; + + struct + { + char catalog[128]; + drflac_uint64 leadInSampleCount; + drflac_bool32 isCD; + drflac_uint8 trackCount; + const drflac_uint8* pTrackData; + } cuesheet; + + struct + { + drflac_uint32 type; + drflac_uint32 mimeLength; + const char* mime; + drflac_uint32 descriptionLength; + const char* description; + drflac_uint32 width; + drflac_uint32 height; + drflac_uint32 colorDepth; + drflac_uint32 indexColorCount; + drflac_uint32 pictureDataSize; + const drflac_uint8* pPictureData; + } picture; + } data; +} drflac_metadata; + + +/* Callback for when data needs to be read from the client. + * + * pUserData [in] The user data that was passed to drflac_open() and family. + * pBufferOut [out] The output buffer. + * bytesToRead [in] The number of bytes to read. + * + * Returns the number of bytes actually read. + * + * A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until + * either the entire bytesToRead is filled or you have reached the end of the stream. + */ +typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +/* Callback for when data needs to be seeked. + * + * pUserData [in] The user data that was passed to drflac_open() and family. + * offset [in] The number of bytes to move, relative to the origin. Will never be negative. + * origin [in] The origin of the seek - the current position or the start of the stream. + * + * Returns whether or not the seek was successful. + * + * The offset will never be negative. Whether or not it is relative to the beginning or current position is determined + * by the "origin" parameter which will be either drflac_seek_origin_start or drflac_seek_origin_current. + */ +typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); + +/* Callback for when a metadata block is read. + * + * pUserData [in] The user data that was passed to drflac_open() and family. + * pMetadata [in] A pointer to a structure containing the data of the metadata block. + * + * Use pMetadata->type to determine which metadata block is being handled and how to read the data. + */ +typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); + + +/* Structure for internal use. Only used for decoders opened with drflac_open_memory. */ +typedef struct +{ + const drflac_uint8* data; + size_t dataSize; + size_t currentReadPos; +} drflac__memory_stream; + +/* Structure for internal use. Used for bit streaming. */ +typedef struct +{ + /* The function to call when more data needs to be read. */ + drflac_read_proc onRead; + + /* The function to call when the current read position needs to be moved. */ + drflac_seek_proc onSeek; + + /* The user data to pass around to onRead and onSeek. */ + void* pUserData; + + + // The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the + // stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether + // or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t). + size_t unalignedByteCount; + + // The content of the unaligned bytes. + drflac_cache_t unalignedCache; + + // The index of the next valid cache line in the "L2" cache. + drflac_uint32 nextL2Line; + + // The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. + drflac_uint32 consumedBits; + + // The cached data which was most recently read from the client. There are two levels of cache. Data flows as such: + // Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions. + drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; + drflac_cache_t cache; + + // CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this + // is reset to 0 at the beginning of each frame. + drflac_uint16 crc16; + drflac_cache_t crc16Cache; // A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. + drflac_uint32 crc16CacheIgnoredBytes; // The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. +} drflac_bs; + +typedef struct +{ + // The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. + drflac_uint8 subframeType; + + // The number of wasted bits per sample as specified by the sub-frame header. + drflac_uint8 wastedBitsPerSample; + + // The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. + drflac_uint8 lpcOrder; + + // The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because + // an extra bit is required for side channels when interchannel decorrelation is being used. + drflac_uint32 bitsPerSample; + + // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. Note that + // it's a signed 32-bit integer for each value. + drflac_int32* pDecodedSamples; +} drflac_subframe; + +typedef struct +{ + // If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will + // always be set to 0. + drflac_uint64 sampleNumber; + + // If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. + drflac_uint32 frameNumber; + + // The sample rate of this frame. + drflac_uint32 sampleRate; + + // The number of samples in each sub-frame within this frame. + drflac_uint16 blockSize; + + // The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this + // will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. + drflac_uint8 channelAssignment; + + // The number of bits per sample within this frame. + drflac_uint8 bitsPerSample; + + // The frame's CRC. + drflac_uint8 crc8; +} drflac_frame_header; + +typedef struct +{ + // The header. + drflac_frame_header header; + + // The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples + // are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. + drflac_uint32 samplesRemaining; + + // The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. + drflac_subframe subframes[8]; +} drflac_frame; + +typedef struct +{ + // The function to call when a metadata block is read. + drflac_meta_proc onMeta; + + // The user data posted to the metadata callback function. + void* pUserDataMD; + + + // The sample rate. Will be set to something like 44100. + drflac_uint32 sampleRate; + + // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the + // value specified in the STREAMINFO block. + drflac_uint8 channels; + + // The bits per sample. Will be set to somthing like 16, 24, etc. + drflac_uint8 bitsPerSample; + + // The maximum block size, in samples. This number represents the number of samples in each channel (not combined). + drflac_uint16 maxBlockSize; + + // The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels, + // with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a + // valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio. + drflac_uint64 totalSampleCount; + + + // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. + drflac_container container; + + + // The position of the seektable in the file. + drflac_uint64 seektablePos; + + // The size of the seektable. + drflac_uint32 seektableSize; + + + // Information about the frame the decoder is currently sitting on. + drflac_frame currentFrame; + + // The position of the first frame in the stream. This is only ever used for seeking. + drflac_uint64 firstFramePos; + + + // A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). + drflac__memory_stream memoryStream; + + + // A pointer to the decoded sample data. This is an offset of pExtraData. + drflac_int32* pDecodedSamples; + + // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. + void* _oggbs; + + // The bit streamer. The raw FLAC data is fed through this object. + drflac_bs bs; + + // Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. + drflac_uint8 pExtraData[1]; +} drflac; + + +// Opens a FLAC decoder. +// +// onRead [in] The function to call when data needs to be read from the client. +// onSeek [in] The function to call when the read position of the client data needs to move. +// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. +// +// Returns a pointer to an object representing the decoder. +// +// Close the decoder with drflac_close(). +// +// This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated +// FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with +// multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. +// +// This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory() +// to open the stream from a file or from a block of memory respectively. +// +// The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where +// the header may not be present. +// +// See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close() +drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData); + +// The same as drflac_open(), except attempts to open the stream even when a header block is not present. +// +// Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do +// not set this to drflac_container_unknown - that is for internal use only. +// +// Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never +// found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an +// indicator that the end of the stream was found. +drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData); + +// Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). +// +// onRead [in] The function to call when data needs to be read from the client. +// onSeek [in] The function to call when the read position of the client data needs to move. +// onMeta [in] The function to call for every metadata block. +// pUserData [in, optional] A pointer to application defined data that will be passed to onRead, onSeek and onMeta. +// +// Returns a pointer to an object representing the decoder. +// +// Close the decoder with drflac_close(). +// +// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a DRFLAC_MALLOC() +// and DRFLAC_FREE() for every metadata block except for STREAMINFO and PADDING blocks. +// +// The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function +// returns. +// +// The STREAMINFO block must be present for this to succeed. Use drflac_open_with_metadata_relaxed() to open a FLAC +// stream where the header may not be present. +// +// Note that this will behave inconsistently with drflac_open() if the stream is an Ogg encapsulated stream and a metadata +// block is corrupted. This is due to the way the Ogg stream recovers from corrupted pages. When drflac_open_with_metadata() +// is being used, the open routine will try to read the contents of the metadata block, whereas drflac_open() will simply +// seek past it (for the sake of efficiency). This inconsistency can result in different samples being returned depending on +// whether or not the stream is being opened with metadata. +// +// See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close() +drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData); + +// The same as drflac_open_with_metadata(), except attemps to open the stream even when a header block is not present. +// +// See also: drflac_open_with_metadata(), drflac_open_relaxed() +drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData); + +// Closes the given FLAC decoder. +// +// pFlac [in] The decoder to close. +// +// This will destroy the decoder object. +void drflac_close(drflac* pFlac); + + +// Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. +// +// pFlac [in] The decoder. +// samplesToRead [in] The number of samples to read. +// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. +// +// Returns the number of samples actually read. +// +// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples +// seeked. +drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* pBufferOut); + +// Same as drflac_read_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit. +// +// pFlac [in] The decoder. +// samplesToRead [in] The number of samples to read. +// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. +// +// Returns the number of samples actually read. +// +// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples +// seeked. +// +// Note that this is lossy for streams where the bits per sample is larger than 16. +drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut); + +// Same as drflac_read_s32(), except outputs samples as 32-bit floating-point PCM. +// +// pFlac [in] The decoder. +// samplesToRead [in] The number of samples to read. +// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. +// +// Returns the number of samples actually read. +// +// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples +// seeked. +// +// Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly +// represent every possible number. +drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut); + +// Seeks to the sample at the given index. +// +// pFlac [in] The decoder. +// sampleIndex [in] The index of the sample to seek to. See notes below. +// +// Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise. +// +// The sample index is based on interleaving. In a stereo stream, for example, the sample at index 0 is the first sample +// in the left channel; the sample at index 1 is the first sample on the right channel, and so on. +// +// When seeking, you will likely want to ensure it's rounded to a multiple of the channel count. You can do this with +// something like drflac_seek_to_sample(pFlac, (mySampleIndex + (mySampleIndex % pFlac->channels))) +drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex); + + + +#ifndef DR_FLAC_NO_STDIO +// Opens a FLAC decoder from the file at the given path. +// +// filename [in] The path of the file to open, either absolute or relative to the current directory. +// +// Returns a pointer to an object representing the decoder. +// +// Close the decoder with drflac_close(). +// +// This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the +// number of files a process can have open at any given time, so keep this mind if you have many decoders open at the +// same time. +// +// See also: drflac_open(), drflac_open_file_with_metadata(), drflac_close() +drflac* drflac_open_file(const char* filename); + +// Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) +// +// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. +drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData); +#endif + +// Opens a FLAC decoder from a pre-allocated block of memory +// +// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +// the lifetime of the decoder. +drflac* drflac_open_memory(const void* data, size_t dataSize); + +// Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) +// +// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. +drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData); + + + +//// High Level APIs //// + +// Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a +// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with DRFLAC_FREE(). +// +// Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously +// read samples into a dynamically sized buffer on the heap until no samples are left. +// +// Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). +drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_s32(), except returns signed 16-bit integer samples. +drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_s32(), except returns 32-bit floating-point samples. +float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +#ifndef DR_FLAC_NO_STDIO +// Same as drflac_open_and_decode_s32() except opens the decoder from a file. +drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_file_s32(), except returns signed 16-bit integer samples. +drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_file_f32(), except returns 32-bit floating-point samples. +float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); +#endif + +// Same as drflac_open_and_decode_s32() except opens the decoder from a block of memory. +drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_memory_s32(), except returns signed 16-bit integer samples. +drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_memory_s32(), except returns 32-bit floating-point samples. +float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Frees memory that was allocated internally by dr_flac. +void drflac_free(void* p); + + +// Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. +typedef struct +{ + drflac_uint32 countRemaining; + const char* pRunningData; +} drflac_vorbis_comment_iterator; + +// Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT +// metadata block. +void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments); + +// Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The +// returned string is NOT null terminated. +const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); + + + +#ifdef __cplusplus +} +#endif +#endif //dr_flac_h + + +/////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef DR_FLAC_IMPLEMENTATION +#include +#include + +// CPU architecture. +#if defined(__x86_64__) || defined(_M_X64) +#define DRFLAC_X64 +#elif defined(__i386) || defined(_M_IX86) +#define DRFLAC_X86 +#elif defined(__arm__) || defined(_M_ARM) +#define DRFLAC_ARM +#endif + +// Compile-time CPU feature support. +#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) + #ifdef _MSC_VER + #if _MSC_VER >= 1400 + #include + static void drflac__cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define DRFLAC_NO_CPUID + #endif + #else + #if defined(__GNUC__) || defined(__clang__) + static void drflac__cpuid(int info[4], int fid) + { + asm ( + "movl %[fid], %%eax\n\t" + "cpuid\n\t" + "movl %%eax, %[info0]\n\t" + "movl %%ebx, %[info1]\n\t" + "movl %%ecx, %[info2]\n\t" + "movl %%edx, %[info3]\n\t" + : [info0] "=rm"(info[0]), + [info1] "=rm"(info[1]), + [info2] "=rm"(info[2]), + [info3] "=rm"(info[3]) + : [fid] "rm"(fid) + : "eax", "ebx", "ecx", "edx" + ); + } + #else + #define DRFLAC_NO_CPUID + #endif + #endif +#else +#define DRFLAC_NO_CPUID +#endif + + +#ifdef __linux__ +#define _BSD_SOURCE +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) +#define DRFLAC_HAS_LZCNT_INTRINSIC +#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) +#define DRFLAC_HAS_LZCNT_INTRINSIC +#elif defined(__clang__) + #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) + #define DRFLAC_HAS_LZCNT_INTRINSIC + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1300 +#define DRFLAC_HAS_BYTESWAP_INTRINSIC +#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +#define DRFLAC_HAS_BYTESWAP_INTRINSIC +#elif defined(__clang__) + #if __has_builtin(__builtin_bswap16) && __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64) + #define DRFLAC_HAS_BYTESWAP_INTRINSIC + #endif +#endif + + +// Standard library stuff. +#ifndef DRFLAC_ASSERT +#include +#define DRFLAC_ASSERT(expression) assert(expression) +#endif +#ifndef DRFLAC_MALLOC +#define DRFLAC_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRFLAC_REALLOC +#define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRFLAC_FREE +#define DRFLAC_FREE(p) free((p)) +#endif +#ifndef DRFLAC_COPY_MEMORY +#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRFLAC_ZERO_MEMORY +#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif + +#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future. + +#ifdef _MSC_VER +#define DRFLAC_INLINE __forceinline +#else +#ifdef __GNUC__ +#define DRFLAC_INLINE inline __attribute__((always_inline)) +#else +#define DRFLAC_INLINE inline +#endif +#endif + +typedef drflac_int32 drflac_result; +#define DRFLAC_SUCCESS 0 +#define DRFLAC_ERROR -1 // A generic error. +#define DRFLAC_INVALID_ARGS -2 +#define DRFLAC_END_OF_STREAM -128 +#define DRFLAC_CRC_MISMATCH -129 + +#define DRFLAC_SUBFRAME_CONSTANT 0 +#define DRFLAC_SUBFRAME_VERBATIM 1 +#define DRFLAC_SUBFRAME_FIXED 8 +#define DRFLAC_SUBFRAME_LPC 32 +#define DRFLAC_SUBFRAME_RESERVED 255 + +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 + +#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 +#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 +#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 +#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 + + +#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define drflac_assert DRFLAC_ASSERT +#define drflac_copy_memory DRFLAC_COPY_MEMORY +#define drflac_zero_memory DRFLAC_ZERO_MEMORY + + +// CPU caps. +static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; +#ifndef DRFLAC_NO_CPUID +static drflac_bool32 drflac__gIsSSE42Supported = DRFLAC_FALSE; +static void drflac__init_cpu_caps() +{ + int info[4] = {0}; + + // LZCNT + drflac__cpuid(info, 0x80000001); + drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; + + // SSE4.2 + drflac__cpuid(info, 1); + drflac__gIsSSE42Supported = (info[2] & (1 << 19)) != 0; +} +#endif + + +//// Endian Management //// +static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian() +{ +#if defined(DRFLAC_X86) || defined(DRFLAC_X64) + return DRFLAC_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap32(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} + +static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & (drflac_uint64)0xFF00000000000000) >> 56) | + ((n & (drflac_uint64)0x00FF000000000000) >> 40) | + ((n & (drflac_uint64)0x0000FF0000000000) >> 24) | + ((n & (drflac_uint64)0x000000FF00000000) >> 8) | + ((n & (drflac_uint64)0x00000000FF000000) << 8) | + ((n & (drflac_uint64)0x0000000000FF0000) << 24) | + ((n & (drflac_uint64)0x000000000000FF00) << 40) | + ((n & (drflac_uint64)0x00000000000000FF) << 56); +#endif +} + + +static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) +{ +#ifdef __linux__ + return be16toh(n); +#else + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint16(n); + } + + return n; +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) +{ +#ifdef __linux__ + return be32toh(n); +#else + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +#endif +} + +static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) +{ +#ifdef __linux__ + return be64toh(n); +#else + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint64(n); + } + + return n; +#endif +} + + +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) +{ +#ifdef __linux__ + return le32toh(n); +#else + if (!drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +#endif +} + + +static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) +{ + drflac_uint32 result = 0; + result |= (n & 0x7F000000) >> 3; + result |= (n & 0x007F0000) >> 2; + result |= (n & 0x00007F00) >> 1; + result |= (n & 0x0000007F) >> 0; + + return result; +} + + + +// The CRC code below is based on this document: http://zlib.net/crc_v3.txt +static drflac_uint8 drflac__crc8_table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +static drflac_uint16 drflac__crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; + +static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) +{ + return drflac__crc8_table[crc ^ data]; +} + +static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) +{ + drflac_assert(count <= 32); + +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") + drflac_uint8 p = 0x07; + for (int i = count-1; i >= 0; --i) { + drflac_uint8 bit = (data & (1 << i)) >> i; + if (crc & 0x80) { + crc = ((crc << 1) | bit) ^ p; + } else { + crc = ((crc << 1) | bit); + } + } + return crc; +#else + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) +{ + return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) +{ + switch (byteCount) + { +#ifdef DRFLAC_64BIT + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#endif + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + } + + return crc; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) +{ + drflac_assert(count <= 64); + +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") + drflac_uint16 p = 0x8005; + for (int i = count-1; i >= 0; --i) { + drflac_uint16 bit = (data & (1ULL << i)) >> i; + if (r & 0x8000) { + r = ((r << 1) | bit) ^ p; + } else { + r = ((r << 1) | bit); + } + } + + return crc; +#else + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) +{ + drflac_assert(count <= 64); + +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0xFF00000000000000 << leftoverBits)) >> (56 + leftoverBits))); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00FF000000000000 << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000FF0000000000 << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000FF00000000 << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000FF000000 << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000000000FF0000 << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000000000FF00 << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000000000FF << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +} + + +static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) +{ +#ifdef DRFLAC_64BIT + return drflac_crc16__64bit(crc, data, count); +#else + return drflac_crc16__32bit(crc, data, count); +#endif +} + + +#ifdef DRFLAC_64BIT +#define drflac__be2host__cache_line drflac__be2host_64 +#else +#define drflac__be2host__cache_line drflac__be2host_32 +#endif + +// BIT READING ATTEMPT #2 +// +// This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting +// on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache +// is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an +// array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data +// from onRead() is read into. +#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - ((bs)->consumedBits)) +#ifdef DRFLAC_64BIT +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((drflac_uint64)-1LL) >> (_bitCount))) +#else +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((drflac_uint32)-1) >> (_bitCount))) +#endif +#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), _bitCount) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), _bitCount)) +#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) + + +#ifndef DR_FLAC_NO_CRC +static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) +{ + bs->crc16 = 0; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +} + +static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) +{ + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16CacheIgnoredBytes = 0; +} + +static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) +{ + // We should never be flushing in a situation where we are not aligned on a byte boundary. + drflac_assert((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + + // The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined + // by the number of bits that have been consumed. + if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + drflac__update_crc16(bs); + } else { + // We only accumulate the consumed bits. + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + + // The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated + // so we can handle that later. + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; + } + + return bs->crc16; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) +{ + // Fast path. Try loading straight from L2. + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } + + // If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's + // any left. + if (bs->unalignedByteCount > 0) { + return DRFLAC_FALSE; // If we have any unaligned bytes it means there's no more aligned bytes left in the client. + } + + size_t bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); + + bs->nextL2Line = 0; + if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } + + + // If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably + // means we've just reached the end of the file. We need to move the valid data down to the end of the buffer + // and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to + // the size of the L1 so we'll need to seek backwards by any misaligned bytes. + size_t alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); + + // We need to keep track of any unaligned bytes for later use. + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + if (bs->unalignedByteCount > 0) { + bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; + } + + if (alignedL1LineCount > 0) { + size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + for (size_t i = alignedL1LineCount; i > 0; --i) { + bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; + } + + bs->nextL2Line = (drflac_uint32)offset; + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } else { + // If we get into this branch it means we weren't able to load any L1-aligned data. + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); + return DRFLAC_FALSE; + } +} + +static drflac_bool32 drflac__reload_cache(drflac_bs* bs) +{ +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); +#endif + + // Fast path. Try just moving the next value in the L2 cache to the L1 cache. + if (drflac__reload_l1_cache_from_l2(bs)) { + bs->cache = drflac__be2host__cache_line(bs->cache); + bs->consumedBits = 0; +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + return DRFLAC_TRUE; + } + + // Slow path. + + // If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last + // few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the + // data from the unaligned cache. + size_t bytesRead = bs->unalignedByteCount; + if (bytesRead == 0) { + return DRFLAC_FALSE; + } + + drflac_assert(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + + bs->cache = drflac__be2host__cache_line(bs->unalignedCache); + bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs->consumedBits); // <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. + bs->unalignedByteCount = 0; // <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache >> bs->consumedBits; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +#endif + return DRFLAC_TRUE; +} + +static void drflac__reset_cache(drflac_bs* bs) +{ + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); // <-- This clears the L2 cache. + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); // <-- This clears the L1 cache. + bs->cache = 0; + bs->unalignedByteCount = 0; // <-- This clears the trailing unaligned bytes. + bs->unalignedCache = 0; + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = 0; + bs->crc16CacheIgnoredBytes = 0; +#endif +} + + +static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) +{ + drflac_assert(bs != NULL); + drflac_assert(pResultOut != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 32); + + if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; + } else { + *pResultOut = (drflac_uint32)bs->cache; + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + bs->cache = 0; + } + return DRFLAC_TRUE; + } else { + // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. + drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); + drflac_uint32 bitCountLo = bitCount - bitCountHi; + drflac_uint32 resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + *pResultOut = (resultHi << bitCountLo) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 32); + + drflac_uint32 result; + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + drflac_uint32 signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + + *pResult = (drflac_int32)result; + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) +{ + drflac_assert(bitCount <= 64); + drflac_assert(bitCount > 32); + + drflac_uint32 resultHi; + if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { + return DRFLAC_FALSE; + } + + drflac_uint32 resultLo; + if (!drflac__read_uint32(bs, 32, &resultLo)) { + return DRFLAC_FALSE; + } + + *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); + return DRFLAC_TRUE; +} + +// Function below is unused, but leaving it here in case I need to quickly add it again. +#if 0 +static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) +{ + drflac_assert(bitCount <= 64); + + drflac_uint64 result; + if (!drflac__read_uint64(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + drflac_uint64 signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + + *pResultOut = (drflac_int64)result; + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 16); + + drflac_uint32 result; + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_uint16)result; + return DRFLAC_TRUE; +} + +#if 0 +static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 16); + + drflac_int32 result; + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_int16)result; + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 8); + + drflac_uint32 result; + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_uint8)result; + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 8); + + drflac_int32 result; + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_int8)result; + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) +{ + if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (drflac_uint32)bitsToSeek; + bs->cache <<= bitsToSeek; + return DRFLAC_TRUE; + } else { + // It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. + bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->cache = 0; + + // Simple case. Seek in groups of the same number as bits that fit within a cache line. +#ifdef DRFLAC_64BIT + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint64 bin; + if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#else + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint32 bin; + if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#endif + + // Whole leftover bytes. + while (bitsToSeek >= 8) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, 8, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= 8; + } + + // Leftover bits. + if (bitsToSeek > 0) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek = 0; // <-- Necessary for the assert below. + } + + drflac_assert(bitsToSeek == 0); + return DRFLAC_TRUE; + } +} + + +// This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. +static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) +{ + drflac_assert(bs != NULL); + + // The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first + // thing to do is align to the next byte. + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + + for (;;) { +#ifndef DR_FLAC_NO_CRC + drflac__reset_crc16(bs); +#endif + + drflac_uint8 hi; + if (!drflac__read_uint8(bs, 8, &hi)) { + return DRFLAC_FALSE; + } + + if (hi == 0xFF) { + drflac_uint8 lo; + if (!drflac__read_uint8(bs, 6, &lo)) { + return DRFLAC_FALSE; + } + + if (lo == 0x3E) { + return DRFLAC_TRUE; + } else { + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + } + } + } + + // Should never get here. + //return DRFLAC_FALSE; +} + + +#if !defined(DR_FLAC_NO_SIMD) && defined(DRFLAC_HAS_LZCNT_INTRINSIC) +#define DRFLAC_IMPLEMENT_CLZ_LZCNT +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) +#define DRFLAC_IMPLEMENT_CLZ_MSVC +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) +{ + static drflac_uint32 clz_table_4[] = { + 0, + 4, + 3, 3, + 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1 + }; + + drflac_uint32 n = clz_table_4[x >> (sizeof(x)*8 - 4)]; + if (n == 0) { +#ifdef DRFLAC_64BIT + if ((x & 0xFFFFFFFF00000000ULL) == 0) { n = 32; x <<= 32; } + if ((x & 0xFFFF000000000000ULL) == 0) { n += 16; x <<= 16; } + if ((x & 0xFF00000000000000ULL) == 0) { n += 8; x <<= 8; } + if ((x & 0xF000000000000000ULL) == 0) { n += 4; x <<= 4; } +#else + if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } + if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } + if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } +#endif + n += clz_table_4[x >> (sizeof(x)*8 - 4)]; + } + + return n - 1; +} + +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT +static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported() +{ + // If the compiler itself does not support the intrinsic then we'll need to return false. +#ifdef DRFLAC_HAS_LZCNT_INTRINSIC + return drflac__gIsLZCNTSupported; +#else + return DRFLAC_FALSE; +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) +{ +#if defined(_MSC_VER) && !defined(__clang__) + #ifdef DRFLAC_64BIT + return (drflac_uint32)__lzcnt64(x); + #else + return (drflac_uint32)__lzcnt(x); + #endif +#else + #if defined(__GNUC__) || defined(__clang__) + #ifdef DRFLAC_64BIT + return (drflac_uint32)__builtin_clzll((unsigned long long)x); + #else + return (drflac_uint32)__builtin_clzl((unsigned long)x); + #endif + #else + // Unsupported compiler. + #error "This compiler does not support the lzcnt intrinsic." + #endif +#endif +} +#endif + +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC +static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) +{ + drflac_uint32 n; +#ifdef DRFLAC_64BIT + _BitScanReverse64((unsigned long*)&n, x); +#else + _BitScanReverse((unsigned long*)&n, x); +#endif + return sizeof(x)*8 - n - 1; +} +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) +{ + // This function assumes at least one bit is set. Checking for 0 needs to be done at a higher level, outside this function. +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT + if (drflac__is_lzcnt_supported()) { + return drflac__clz_lzcnt(x); + } else +#endif + { + #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC + return drflac__clz_msvc(x); + #else + return drflac__clz_software(x); + #endif + } +} + + +static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +{ + drflac_uint32 zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; + + bs->consumedBits += setBitOffsetPlus1; + bs->cache <<= setBitOffsetPlus1; + + *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; + return DRFLAC_TRUE; +} + + + +static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) +{ + drflac_assert(bs != NULL); + drflac_assert(offsetFromStart > 0); + + // Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which + // is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit. + // To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder. + if (offsetFromStart > 0x7FFFFFFF) { + drflac_uint64 bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + + while (bytesRemaining > 0x7FFFFFFF) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + } + + if (bytesRemaining > 0) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + } + + // The cache should be reset to force a reload of fresh data from the client. + drflac__reset_cache(bs); + return DRFLAC_TRUE; +} + + +static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) +{ + drflac_assert(bs != NULL); + drflac_assert(pNumberOut != NULL); + + drflac_uint8 crc = *pCRCOut; + + unsigned char utf8[7] = {0}; + if (!drflac__read_uint8(bs, 8, utf8)) { + *pNumberOut = 0; + return DRFLAC_END_OF_STREAM; + } + crc = drflac_crc8(crc, utf8[0], 8); + + if ((utf8[0] & 0x80) == 0) { + *pNumberOut = utf8[0]; + *pCRCOut = crc; + return DRFLAC_SUCCESS; + } + + int byteCount = 1; + if ((utf8[0] & 0xE0) == 0xC0) { + byteCount = 2; + } else if ((utf8[0] & 0xF0) == 0xE0) { + byteCount = 3; + } else if ((utf8[0] & 0xF8) == 0xF0) { + byteCount = 4; + } else if ((utf8[0] & 0xFC) == 0xF8) { + byteCount = 5; + } else if ((utf8[0] & 0xFE) == 0xFC) { + byteCount = 6; + } else if ((utf8[0] & 0xFF) == 0xFE) { + byteCount = 7; + } else { + *pNumberOut = 0; + return DRFLAC_CRC_MISMATCH; // Bad UTF-8 encoding. + } + + // Read extra bytes. + drflac_assert(byteCount > 1); + + drflac_uint64 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + for (int i = 1; i < byteCount; ++i) { + if (!drflac__read_uint8(bs, 8, utf8 + i)) { + *pNumberOut = 0; + return DRFLAC_END_OF_STREAM; + } + crc = drflac_crc8(crc, utf8[i], 8); + + result = (result << 6) | (utf8[i] & 0x3F); + } + + *pNumberOut = result; + *pCRCOut = crc; + return DRFLAC_SUCCESS; +} + + + + +// The next two functions are responsible for calculating the prediction. +// +// When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's +// safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_assert(order <= 32); + + // 32-bit version. + + // VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. + drflac_int32 prediction = 0; + + switch (order) + { + case 32: prediction += coefficients[31] * pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; + } + + return (drflac_int32)(prediction >> shift); +} + +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_assert(order <= 32); + + // 64-bit version. + + // This method is faster on the 32-bit build when compiling with VC++. See note below. +#ifndef DRFLAC_64BIT + drflac_int64 prediction; + if (order == 8) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + } + else if (order == 7) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + } + else if (order == 3) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + } + else if (order == 6) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + } + else if (order == 5) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + } + else if (order == 4) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + } + else if (order == 12) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + } + else if (order == 2) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + } + else if (order == 1) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + } + else if (order == 10) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + } + else if (order == 9) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + } + else if (order == 11) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + } + else + { + prediction = 0; + for (int j = 0; j < (int)order; ++j) { + prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; + } + } +#endif + + // VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some + // reason. The ugly version above is faster so we'll just switch between the two depending on the target platform. +#ifdef DRFLAC_64BIT + drflac_int64 prediction = 0; + + switch (order) + { + case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; + } +#endif + + return (drflac_int32)(prediction >> shift); +} + +#if 0 +// Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the +// sake of readability and should only be used as a reference. +static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(pSamplesOut != NULL); + + for (drflac_uint32 i = 0; i < count; ++i) { + drflac_uint32 zeroCounter = 0; + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + drflac_uint32 decodedRice; + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + decodedRice |= (zeroCounter << riceParam); + if ((decodedRice & 0x01)) { + decodedRice = ~(decodedRice >> 1); + } else { + decodedRice = (decodedRice >> 1); + } + + + if (bitsPerSample > 16) { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + } + } + + return DRFLAC_TRUE; +} +#endif + +#if 0 +static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_uint32 zeroCounter = 0; + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + drflac_uint32 decodedRice; + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = decodedRice; + return DRFLAC_TRUE; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); + drflac_cache_t resultHiShift = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParam; + + + drflac_uint32 zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; + + + drflac_uint32 riceParamPart; + drflac_uint32 riceLength = setBitOffsetPlus1 + riceParam; + if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> (DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceLength)); + + bs->consumedBits += riceLength; + bs->cache <<= riceLength; + } else { + bs->consumedBits += riceLength; + if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + bs->cache <<= setBitOffsetPlus1; + } + + // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. + drflac_uint32 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); + drflac_cache_t resultHi = bs->cache & riceParamMask; // <-- This mask is OK because all bits after the first bits are always zero. + + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->consumedBits = 0; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; + #endif + } else { + // Slow path. We need to fetch more data from the client. + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + riceParamPart = (drflac_uint32)((resultHi >> resultHiShift) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo)); + + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + } + + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = riceParamPart; + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(pSamplesOut != NULL); + + drflac_uint32 zeroCountPart; + drflac_uint32 riceParamPart; + + drflac_uint32 i = 0; + while (i < count) { + // Rice extraction. + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { + return DRFLAC_FALSE; + } + + // Rice reconstruction. + static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + riceParamPart |= (zeroCountPart << riceParam); + riceParamPart = (riceParamPart >> 1) ^ t[riceParamPart & 0x01]; + //riceParamPart = (riceParamPart >> 1) ^ (~(riceParamPart & 0x01) + 1); + + // Sample reconstruction. + if (bitsPerSample > 16) { + pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + } + + i += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ +#if 0 + return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); +#else + return drflac__decode_samples_with_residual__rice__simple(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); +#endif +} + +// Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. +static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + + for (drflac_uint32 i = 0; i < count; ++i) { + drflac_uint32 zeroCountPart; + drflac_uint32 riceParamPart; + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(unencodedBitsPerSample > 0 && unencodedBitsPerSample <= 32); + drflac_assert(pSamplesOut != NULL); + + for (unsigned int i = 0; i < count; ++i) { + if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return DRFLAC_FALSE; + } + + if (bitsPerSample > 16) { + pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + } + } + + return DRFLAC_TRUE; +} + + +// Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called +// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The +// and parameters are used to determine how many residual values need to be decoded. +static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_assert(bs != NULL); + drflac_assert(blockSize != 0); + drflac_assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? + + drflac_uint8 residualMethod; + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; // Unknown or unsupported residual coding method. + } + + // Ignore the first values. + pDecodedSamples += order; + + + drflac_uint8 partitionOrder; + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; + } + + // From the FLAC spec: + // The Rice partition order in a Rice-coded residual section must be less than or equal to 8. + if (partitionOrder > 8) { + return DRFLAC_FALSE; + } + + // Validation check. + if ((blockSize / (1 << partitionOrder)) <= order) { + return DRFLAC_FALSE; + } + + drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + drflac_uint32 partitionsRemaining = (1 << partitionOrder); + for (;;) { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 16) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 32) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + } else { + unsigned char unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; + } + + if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + } + + pDecodedSamples += samplesInPartition; + + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + + if (partitionOrder != 0) { + samplesInPartition = blockSize / (1 << partitionOrder); + } + } + + return DRFLAC_TRUE; +} + +// Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called +// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be set to 0. The +// and parameters are used to determine how many residual values need to be decoded. +static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) +{ + drflac_assert(bs != NULL); + drflac_assert(blockSize != 0); + + drflac_uint8 residualMethod; + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; // Unknown or unsupported residual coding method. + } + + drflac_uint8 partitionOrder; + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; + } + + drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + drflac_uint32 partitionsRemaining = (1 << partitionOrder); + for (;;) + { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 16) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 32) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return DRFLAC_FALSE; + } + } else { + unsigned char unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; + } + + if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return DRFLAC_FALSE; + } + } + + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + samplesInPartition = blockSize / (1 << partitionOrder); + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples) +{ + // Only a single sample needs to be decoded here. + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + // We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely) + // we'll want to look at a more efficient way. + for (drflac_uint32 i = 0; i < blockSize; ++i) { + pDecodedSamples[i] = sample; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples) +{ + for (drflac_uint32 i = 0; i < blockSize; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +{ + drflac_int32 lpcCoefficientsTable[5][4] = { + {0, 0, 0, 0}, + {1, 0, 0, 0}, + {2, -1, 0, 0}, + {3, -3, 1, 0}, + {4, -6, 4, -1} + }; + + // Warm up samples and coefficients. + for (drflac_uint32 i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +{ + drflac_uint8 i; + + // Warm up samples. + for (i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + drflac_uint8 lpcPrecision; + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; + } + if (lpcPrecision == 15) { + return DRFLAC_FALSE; // Invalid. + } + lpcPrecision += 1; + + + drflac_int8 lpcShift; + if (!drflac__read_int8(bs, 5, &lpcShift)) { + return DRFLAC_FALSE; + } + + + drflac_int32 coefficients[32]; + for (i = 0; i < lpcOrder; ++i) { + if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { + return DRFLAC_FALSE; + } + } + + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) +{ + drflac_assert(bs != NULL); + drflac_assert(header != NULL); + + const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; // -1 = reserved. + + // Keep looping until we find a valid sync code. + for (;;) { + if (!drflac__find_and_seek_to_next_sync_code(bs)) { + return DRFLAC_FALSE; + } + + drflac_uint8 crc8 = 0xCE; // 0xCE = drflac_crc8(0, 0x3FFE, 14); + + drflac_uint8 reserved = 0; + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, reserved, 1); + + + drflac_uint8 blockingStrategy = 0; + if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, blockingStrategy, 1); + + + drflac_uint8 blockSize = 0; + if (!drflac__read_uint8(bs, 4, &blockSize)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, blockSize, 4); + + + drflac_uint8 sampleRate = 0; + if (!drflac__read_uint8(bs, 4, &sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, sampleRate, 4); + + + drflac_uint8 channelAssignment = 0; + if (!drflac__read_uint8(bs, 4, &channelAssignment)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, channelAssignment, 4); + + + drflac_uint8 bitsPerSample = 0; + if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, bitsPerSample, 3); + + + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, reserved, 1); + + + drflac_bool32 isVariableBlockSize = blockingStrategy == 1; + if (isVariableBlockSize) { + drflac_uint64 sampleNumber; + drflac_result result = drflac__read_utf8_coded_number(bs, &sampleNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_END_OF_STREAM) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->frameNumber = 0; + header->sampleNumber = sampleNumber; + } else { + drflac_uint64 frameNumber = 0; + drflac_result result = drflac__read_utf8_coded_number(bs, &frameNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_END_OF_STREAM) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->frameNumber = (drflac_uint32)frameNumber; // <-- Safe cast. + header->sampleNumber = 0; + } + + + if (blockSize == 1) { + header->blockSize = 192; + } else if (blockSize >= 2 && blockSize <= 5) { + header->blockSize = 576 * (1 << (blockSize - 2)); + } else if (blockSize == 6) { + if (!drflac__read_uint16(bs, 8, &header->blockSize)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSize, 8); + header->blockSize += 1; + } else if (blockSize == 7) { + if (!drflac__read_uint16(bs, 16, &header->blockSize)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSize, 16); + header->blockSize += 1; + } else { + header->blockSize = 256 * (1 << (blockSize - 8)); + } + + + if (sampleRate <= 11) { + header->sampleRate = sampleRateTable[sampleRate]; + } else if (sampleRate == 12) { + if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 8); + header->sampleRate *= 1000; + } else if (sampleRate == 13) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + } else if (sampleRate == 14) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + header->sampleRate *= 10; + } else { + continue; // Invalid. Assume an invalid block. + } + + + header->channelAssignment = channelAssignment; + + header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; + if (header->bitsPerSample == 0) { + header->bitsPerSample = streaminfoBitsPerSample; + } + + if (!drflac__read_uint8(bs, 8, &header->crc8)) { + return DRFLAC_FALSE; + } + + #ifndef DR_FLAC_NO_CRC + if (header->crc8 != crc8) { + continue; // CRC mismatch. Loop back to the top and find the next sync code. + } + #endif + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) +{ + drflac_uint8 header; + if (!drflac__read_uint8(bs, 8, &header)) { + return DRFLAC_FALSE; + } + + // First bit should always be 0. + if ((header & 0x80) != 0) { + return DRFLAC_FALSE; + } + + int type = (header & 0x7E) >> 1; + if (type == 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; + } else if (type == 1) { + pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; + } else { + if ((type & 0x20) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (type & 0x1F) + 1; + } else if ((type & 0x08) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (type & 0x07); + if (pSubframe->lpcOrder > 4) { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + pSubframe->lpcOrder = 0; + } + } else { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + } + } + + if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { + return DRFLAC_FALSE; + } + + // Wasted bits per sample. + pSubframe->wastedBitsPerSample = 0; + if ((header & 0x01) == 1) { + unsigned int wastedBitsPerSample; + if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { + return DRFLAC_FALSE; + } + pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(frame != NULL); + + drflac_subframe* pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; + } + + // Side channels require an extra bit per sample. Took a while to figure that one out... + pSubframe->bitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + pSubframe->bitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + pSubframe->bitsPerSample += 1; + } + + // Need to handle wasted bits per sample. + pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pDecodedSamples = pDecodedSamplesOut; + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + drflac__decode_samples__constant(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples); + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + drflac__decode_samples__verbatim(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples); + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + drflac__decode_samples__fixed(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples); + } break; + + case DRFLAC_SUBFRAME_LPC: + { + drflac__decode_samples__lpc(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples); + } break; + + default: return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) +{ + drflac_assert(bs != NULL); + drflac_assert(frame != NULL); + + drflac_subframe* pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; + } + + // Side channels require an extra bit per sample. Took a while to figure that one out... + pSubframe->bitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + pSubframe->bitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + pSubframe->bitsPerSample += 1; + } + + // Need to handle wasted bits per sample. + pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pDecodedSamples = NULL; + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + unsigned int bitsToSeek = frame->header.blockSize * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_LPC: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + unsigned char lpcPrecision; + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; + } + if (lpcPrecision == 15) { + return DRFLAC_FALSE; // Invalid. + } + lpcPrecision += 1; + + + bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; // +5 for shift. + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; + } + } break; + + default: return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + + +static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) +{ + drflac_assert(channelAssignment <= 10); + + drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + return lookup[channelAssignment]; +} + +static drflac_result drflac__decode_frame(drflac* pFlac) +{ + // This function should be called while the stream is sitting on the first byte after the frame header. + drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes)); + + // The frame block size must never be larger than the maximum block size defined by the FLAC stream. + if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) { + return DRFLAC_ERROR; + } + + // The number of channels in the frame must match the channel count from the STREAMINFO block. + int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + if (channelCount != (int)pFlac->channels) { + return DRFLAC_ERROR; + } + + for (int i = 0; i < channelCount; ++i) { + if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) { + return DRFLAC_ERROR; + } + } + + drflac_uint8 paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7; + if (paddingSizeInBits > 0) { + drflac_uint8 padding = 0; + if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return DRFLAC_END_OF_STREAM; + } + } + +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + drflac_uint16 desiredCRC16; + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_END_OF_STREAM; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; // CRC mismatch. + } +#endif + + pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount; + + return DRFLAC_SUCCESS; +} + +static drflac_result drflac__seek_frame(drflac* pFlac) +{ + int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + for (int i = 0; i < channelCount; ++i) { + if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) { + return DRFLAC_ERROR; + } + } + + // Padding. + if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return DRFLAC_ERROR; + } + + // CRC. +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + drflac_uint16 desiredCRC16; + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_END_OF_STREAM; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; // CRC mismatch. + } +#endif + + return DRFLAC_SUCCESS; +} + +static drflac_bool32 drflac__read_and_decode_next_frame(drflac* pFlac) +{ + drflac_assert(pFlac != NULL); + + for (;;) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + + drflac_result result = drflac__decode_frame(pFlac); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Skip to the next frame. + } else { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; + } +} + + +static void drflac__get_current_frame_sample_range(drflac* pFlac, drflac_uint64* pFirstSampleInFrameOut, drflac_uint64* pLastSampleInFrameOut) +{ + drflac_assert(pFlac != NULL); + + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + + drflac_uint64 firstSampleInFrame = pFlac->currentFrame.header.sampleNumber; + if (firstSampleInFrame == 0) { + firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount; + } + + drflac_uint64 lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount); + if (lastSampleInFrame > 0) { + lastSampleInFrame -= 1; // Needs to be zero based. + } + + if (pFirstSampleInFrameOut) *pFirstSampleInFrameOut = firstSampleInFrame; + if (pLastSampleInFrameOut) *pLastSampleInFrameOut = lastSampleInFrame; +} + +static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) +{ + drflac_assert(pFlac != NULL); + + drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); + + drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame)); + return result; +} + +static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac) +{ + // This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. + drflac_assert(pFlac != NULL); + return drflac__seek_frame(pFlac); +} + +static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex) +{ + // We need to find the frame that contains the sample. To do this, we iterate over each frame and inspect it's header. If based on the + // header we can determine that the frame contains the sample, we do a full decode of that frame. + if (!drflac__seek_to_first_frame(pFlac)) { + return DRFLAC_FALSE; + } + + drflac_uint64 runningSampleCount = 0; + for (;;) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // it never existed and keep iterating. + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. + if (samplesToDecode == 0) { + return DRFLAC_TRUE; + } + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + // frame never existed and leave the running sample count untouched. + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } + } +} + + +static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_uint64 sampleIndex) +{ + drflac_assert(pFlac != NULL); + + if (pFlac->seektablePos == 0) { + return DRFLAC_FALSE; + } + + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->seektablePos)) { + return DRFLAC_FALSE; + } + + // The number of seek points is derived from the size of the SEEKTABLE block. + drflac_uint32 seekpointCount = pFlac->seektableSize / 18; // 18 = the size of each seek point. + if (seekpointCount == 0) { + return DRFLAC_FALSE; // Would this ever happen? + } + + + drflac_seekpoint closestSeekpoint = {0, 0, 0}; + + drflac_uint32 seekpointsRemaining = seekpointCount; + while (seekpointsRemaining > 0) { + drflac_seekpoint seekpoint; + if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) { + break; + } + if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) { + break; + } + if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) { + break; + } + + // Note that the seekpoint sample is based on a single channel. The input sample (sampleIndex) is based on interleaving, thus + // we need to multiple the seekpoint's sample by the channel count. + if (seekpoint.firstSample*pFlac->channels > sampleIndex) { + break; + } + + closestSeekpoint = seekpoint; + seekpointsRemaining -= 1; + } + + // At this point we should have found the seekpoint closest to our sample. We need to seek to it using basically the same + // technique as we use with the brute force method. + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + closestSeekpoint.frameOffset)) { + return DRFLAC_FALSE; + } + + drflac_uint64 runningSampleCount = closestSeekpoint.firstSample*pFlac->channels; + for (;;) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // it never existed and keep iterating. + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. + if (samplesToDecode == 0) { + return DRFLAC_TRUE; + } + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + // frame never existed and leave the running sample count untouched. + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } + } +} + + +#ifndef DR_FLAC_NO_OGG +typedef struct +{ + drflac_uint8 capturePattern[4]; // Should be "OggS" + drflac_uint8 structureVersion; // Always 0. + drflac_uint8 headerType; + drflac_uint64 granulePosition; + drflac_uint32 serialNumber; + drflac_uint32 sequenceNumber; + drflac_uint32 checksum; + drflac_uint8 segmentCount; + drflac_uint8 segmentTable[255]; +} drflac_ogg_page_header; +#endif + +typedef struct +{ + drflac_read_proc onRead; + drflac_seek_proc onSeek; + drflac_meta_proc onMeta; + drflac_container container; + void* pUserData; + void* pUserDataMD; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalSampleCount; + drflac_uint16 maxBlockSize; + drflac_uint64 runningFilePos; + drflac_bool32 hasStreamInfoBlock; + drflac_bool32 hasMetadataBlocks; + drflac_bs bs; // <-- A bit streamer is required for loading data during initialization. + drflac_frame_header firstFrameHeader; // <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. + +#ifndef DR_FLAC_NO_OGG + drflac_uint32 oggSerial; + drflac_uint64 oggFirstBytePos; + drflac_ogg_page_header oggBosHeader; +#endif +} drflac_init_info; + +static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +{ + blockHeader = drflac__be2host_32(blockHeader); + *isLastBlock = (blockHeader & (0x01 << 31)) >> 31; + *blockType = (blockHeader & (0x7F << 24)) >> 24; + *blockSize = (blockHeader & 0xFFFFFF); +} + +static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +{ + drflac_uint32 blockHeader; + if (onRead(pUserData, &blockHeader, 4) != 4) { + return DRFLAC_FALSE; + } + + drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return DRFLAC_TRUE; +} + +drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +{ + // min/max block size. + drflac_uint32 blockSizes; + if (onRead(pUserData, &blockSizes, 4) != 4) { + return DRFLAC_FALSE; + } + + // min/max frame size. + drflac_uint64 frameSizes = 0; + if (onRead(pUserData, &frameSizes, 6) != 6) { + return DRFLAC_FALSE; + } + + // Sample rate, channels, bits per sample and total sample count. + drflac_uint64 importantProps; + if (onRead(pUserData, &importantProps, 8) != 8) { + return DRFLAC_FALSE; + } + + // MD5 + drflac_uint8 md5[16]; + if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { + return DRFLAC_FALSE; + } + + blockSizes = drflac__be2host_32(blockSizes); + frameSizes = drflac__be2host_64(frameSizes); + importantProps = drflac__be2host_64(importantProps); + + pStreamInfo->minBlockSize = (blockSizes & 0xFFFF0000) >> 16; + pStreamInfo->maxBlockSize = blockSizes & 0x0000FFFF; + pStreamInfo->minFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0xFFFFFF0000000000) >> 40); + pStreamInfo->maxFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0x000000FFFFFF0000) >> 16); + pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (drflac_uint64)0xFFFFF00000000000) >> 44); + pStreamInfo->channels = (drflac_uint8 )((importantProps & (drflac_uint64)0x00000E0000000000) >> 41) + 1; + pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (drflac_uint64)0x000001F000000000) >> 36) + 1; + pStreamInfo->totalSampleCount = (importantProps & (drflac_uint64)0x0000000FFFFFFFFF) * pStreamInfo->channels; + drflac_copy_memory(pStreamInfo->md5, md5, sizeof(md5)); + + return DRFLAC_TRUE; +} + +drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) +{ + drflac_assert(pFlac != NULL); + + // We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that + // we'll be sitting on byte 42. + drflac_uint64 runningFilePos = 42; + drflac_uint64 seektablePos = 0; + drflac_uint32 seektableSize = 0; + + for (;;) { + drflac_uint8 isLastBlock = 0; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(pFlac->bs.onRead, pFlac->bs.pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + runningFilePos += 4; + + + drflac_metadata metadata; + metadata.type = blockType; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + + switch (blockType) + { + case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: + { + if (pFlac->onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); + metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + { + seektablePos = runningFilePos; + seektableSize = blockSize; + + if (pFlac->onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint); + metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; + + // Endian swap. + for (drflac_uint32 iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { + drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample); + pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset); + pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount); + } + + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + { + if (pFlac->onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + const char* pRunningData = (const char*)pRawData; + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.comments = pRunningData; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: + { + if (pFlac->onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + const char* pRunningData = (const char*)pRawData; + drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(drflac_uint64*)pRunningData); pRunningData += 4; + metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; + metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; + metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: + { + if (pFlac->onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + const char* pRunningData = (const char*)pRawData; + metadata.data.picture.type = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.description = pRunningData; + metadata.data.picture.width = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PADDING: + { + if (pFlac->onMeta) { + metadata.data.padding.unused = 0; + + // Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. + } else { + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + } + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_INVALID: + { + // Invalid chunk. Just skip over this one. + if (pFlac->onMeta) { + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. + } + } + } + + default: + { + // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we + // can at the very least report the chunk to the application and let it look at the raw data. + if (pFlac->onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + } + + // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. + if (pFlac->onMeta == NULL && blockSize > 0) { + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; + } + } + + runningFilePos += blockSize; + if (isLastBlock) { + break; + } + } + + pFlac->seektablePos = seektablePos; + pFlac->seektableSize = seektableSize; + pFlac->firstFramePos = runningFilePos; + + return DRFLAC_TRUE; +} + +drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +{ + (void)onSeek; + + // Pre: The bit stream should be sitting just past the 4-byte id header. + + pInit->container = drflac_container_native; + + // The first metadata block should be the STREAMINFO block. + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + if (!relaxed) { + // We're opening in strict mode and the first block is not the STREAMINFO block. Error. + return DRFLAC_FALSE; + } else { + // Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined + // for that frame. + pInit->hasStreamInfoBlock = DRFLAC_FALSE; + pInit->hasMetadataBlocks = DRFLAC_FALSE; + + if (!drflac__read_next_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return DRFLAC_FALSE; // Couldn't find a frame. + } + + if (pInit->firstFrameHeader.bitsPerSample == 0) { + return DRFLAC_FALSE; // Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. + } + + pInit->sampleRate = pInit->firstFrameHeader.sampleRate; + pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; + pInit->maxBlockSize = 65535; // <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo + return DRFLAC_TRUE; + } + } else { + drflac_streaminfo streaminfo; + if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return DRFLAC_FALSE; + } + + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalSampleCount = streaminfo.totalSampleCount; + pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation). + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + + return DRFLAC_TRUE; + } +} + +#ifndef DR_FLAC_NO_OGG +#define DRFLAC_OGG_MAX_PAGE_SIZE 65307 +#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 // CRC-32 of "OggS". + +typedef enum +{ + drflac_ogg_recover_on_crc_mismatch, + drflac_ogg_fail_on_crc_mismatch +} drflac_ogg_crc_mismatch_recovery; + + +static drflac_uint32 drflac__crc32_table[] = { + 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, + 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, + 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, + 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, + 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, + 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, + 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, + 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, + 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, + 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, + 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, + 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, + 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, + 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, + 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, + 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, + 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, + 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, + 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, + 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, + 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, + 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, + 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, + 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, + 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, + 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, + 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, + 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, + 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, + 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, + 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, + 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, + 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, + 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, + 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, + 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, + 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, + 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, + 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, + 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, + 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, + 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, + 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, + 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, + 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, + 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, + 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, + 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, + 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, + 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, + 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, + 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) +{ +#ifndef DR_FLAC_NO_CRC + return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#else + (void)data; + return crc32; +#endif +} + +#if 0 +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) +{ + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); + return crc32; +} + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) +{ + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); + return crc32; +} +#endif + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) +{ + // This can be optimized. + for (drflac_uint32 i = 0; i < dataSize; ++i) { + crc32 = drflac_crc32_byte(crc32, pData[i]); + } + return crc32; +} + + +static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) +{ + return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; +} + +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) +{ + return 27 + pHeader->segmentCount; +} + +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) +{ + drflac_uint32 pageBodySize = 0; + for (int i = 0; i < pHeader->segmentCount; ++i) { + pageBodySize += pHeader->segmentTable[i]; + } + + return pageBodySize; +} + +drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +{ + drflac_assert(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); + + drflac_uint8 data[23]; + if (onRead(pUserData, data, 23) != 23) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += 23; + + pHeader->structureVersion = data[0]; + pHeader->headerType = data[1]; + drflac_copy_memory(&pHeader->granulePosition, &data[ 2], 8); + drflac_copy_memory(&pHeader->serialNumber, &data[10], 4); + drflac_copy_memory(&pHeader->sequenceNumber, &data[14], 4); + drflac_copy_memory(&pHeader->checksum, &data[18], 4); + pHeader->segmentCount = data[22]; + + // Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. + data[18] = 0; + data[19] = 0; + data[20] = 0; + data[21] = 0; + + drflac_uint32 i; + for (i = 0; i < 23; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); + } + + + if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += pHeader->segmentCount; + + for (i = 0; i < pHeader->segmentCount; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + } + + return DRFLAC_SUCCESS; +} + +drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +{ + *pBytesRead = 0; + + drflac_uint8 id[4]; + if (onRead(pUserData, id, 4) != 4) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += 4; + + // We need to read byte-by-byte until we find the OggS capture pattern. + for (;;) { + if (drflac_ogg__is_capture_pattern(id)) { + *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + + drflac_result result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == DRFLAC_SUCCESS) { + return DRFLAC_SUCCESS; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; + } else { + return result; + } + } + } else { + // The first 4 bytes did not equal the capture pattern. Read the next byte and try again. + id[0] = id[1]; + id[1] = id[2]; + id[2] = id[3]; + if (onRead(pUserData, &id[3], 1) != 1) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += 1; + } + } +} + + +// The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works +// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is architecured +// in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type +// dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from +// the physical Ogg bitstream are converted and delivered in native FLAC format. +typedef struct +{ + drflac_read_proc onRead; // The original onRead callback from drflac_open() and family. + drflac_seek_proc onSeek; // The original onSeek callback from drflac_open() and family. + void* pUserData; // The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. + drflac_uint64 currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. + drflac_uint64 firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. + drflac_uint32 serialNumber; // The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. + drflac_ogg_page_header bosPageHeader; // Used for seeking. + drflac_ogg_page_header currentPageHeader; + drflac_uint32 bytesRemainingInPage; + drflac_uint32 pageDataSize; + drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; +} drflac_oggbs; // oggbs = Ogg Bitstream + +static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) +{ + size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); + oggbs->currentBytePos += bytesActuallyRead; + + return bytesActuallyRead; +} + +static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) +{ + if (origin == drflac_seek_origin_start) { + if (offset <= 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos = offset; + + return DRFLAC_TRUE; + } else { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos = offset; + + return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); + } + } else { + while (offset > 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos += 0x7FFFFFFF; + offset -= 0x7FFFFFFF; + } + + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { // <-- Safe cast thanks to the loop above. + return DRFLAC_FALSE; + } + oggbs->currentBytePos += offset; + + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) +{ + drflac_ogg_page_header header; + for (;;) { + drflac_uint32 crc32 = 0; + drflac_uint32 bytesRead; + if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos += bytesRead; + + drflac_uint32 pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { + continue; // Invalid page size. Assume it's corrupted and just move to the next page. + } + + if (header.serialNumber != oggbs->serialNumber) { + // It's not a FLAC page. Skip it. + if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + continue; + } + + + // We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. + if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return DRFLAC_FALSE; + } + oggbs->pageDataSize = pageBodySize; + +#ifndef DR_FLAC_NO_CRC + drflac_uint32 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); + if (actualCRC32 != header.checksum) { + if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { + continue; // CRC mismatch. Skip this page. + } else { + // Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we + // go to the next valid page to ensure we're in a good state, but return false to let the caller know that the + // seek did not fully complete. + drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); + return DRFLAC_FALSE; + } + } +#endif + + oggbs->currentPageHeader = header; + oggbs->bytesRemainingInPage = pageBodySize; + return DRFLAC_TRUE; + } +} + +// Function below is unused at the moment, but I might be re-adding it later. +#if 0 +static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) +{ + drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + drflac_uint8 iSeg = 0; + drflac_uint32 iByte = 0; + while (iByte < bytesConsumedInPage) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (iByte + segmentSize > bytesConsumedInPage) { + break; + } else { + iSeg += 1; + iByte += segmentSize; + } + } + + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); + return iSeg; +} + +static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) +{ + // The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. + for (;;) { + drflac_bool32 atEndOfPage = DRFLAC_FALSE; + + drflac_uint8 bytesRemainingInSeg; + drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + + drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (segmentSize < 255) { + if (iSeg == oggbs->currentPageHeader.segmentCount-1) { + atEndOfPage = DRFLAC_TRUE; + } + + break; + } + + bytesToEndOfPacketOrPage += segmentSize; + } + + // At this point we will have found either the packet or the end of the page. If were at the end of the page we'll + // want to load the next page and keep searching for the end of the packet. + drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); + oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; + + if (atEndOfPage) { + // We're potentially at the next packet, but we need to check the next page first to be sure because the packet may + // straddle pages. + if (!drflac_oggbs__goto_next_page(oggbs)) { + return DRFLAC_FALSE; + } + + // If it's a fresh packet it most likely means we're at the next packet. + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + return DRFLAC_TRUE; + } + } else { + // We're at the next packet. + return DRFLAC_TRUE; + } + } +} + +static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) +{ + // The bitstream should be sitting on the first byte just after the header of the frame. + + // What we're actually doing here is seeking to the start of the next packet. + return drflac_oggbs__seek_to_next_packet(oggbs); +} +#endif + +static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + drflac_assert(oggbs != NULL); + + drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; + + // Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. + size_t bytesRead = 0; + while (bytesRead < bytesToRead) { + size_t bytesRemainingToRead = bytesToRead - bytesRead; + + if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { + drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + bytesRead += bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; + break; + } + + // If we get here it means some of the requested data is contained in the next pages. + if (oggbs->bytesRemainingInPage > 0) { + drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + bytesRead += oggbs->bytesRemainingInPage; + pRunningBufferOut += oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + + drflac_assert(bytesRemainingToRead > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + break; // Failed to go to the next page. Might have simply hit the end of the stream. + } + } + + return bytesRead; +} + +static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + drflac_assert(oggbs != NULL); + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + // Seeking is always forward which makes things a lot simpler. + if (origin == drflac_seek_origin_start) { + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + return DRFLAC_FALSE; + } + + return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); + } + + + drflac_assert(origin == drflac_seek_origin_current); + + int bytesSeeked = 0; + while (bytesSeeked < offset) { + int bytesRemainingToSeek = offset - bytesSeeked; + drflac_assert(bytesRemainingToSeek >= 0); + + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + bytesSeeked += bytesRemainingToSeek; + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } + + // If we get here it means some of the requested data is contained in the next pages. + if (oggbs->bytesRemainingInPage > 0) { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + + drflac_assert(bytesRemainingToSeek > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + // Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; +} + +drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + + drflac_uint64 originalBytePos = oggbs->currentBytePos; // For recovery. + + // First seek to the first frame. + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) { + return DRFLAC_FALSE; + } + oggbs->bytesRemainingInPage = 0; + + drflac_uint64 runningGranulePosition = 0; + drflac_uint64 runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier. + for (;;) { + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DRFLAC_FALSE; // Never did find that sample... + } + + runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) { + break; // The sample is somewhere in the previous page. + } + + + // At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we + // disregard any pages that do not begin a fresh packet. + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { // <-- Is it a fresh page? + if (oggbs->currentPageHeader.segmentTable[0] >= 2) { + drflac_uint8 firstBytesInPage[2]; + firstBytesInPage[0] = oggbs->pageData[0]; + firstBytesInPage[1] = oggbs->pageData[1]; + + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { // <-- Does the page begin with a frame's sync code? + runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels; + } + + continue; + } + } + } + + + // We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the + // start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of + // a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until + // we find the one containing the target sample. + if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + return DRFLAC_FALSE; + } + + + // At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep + // looping over these frames until we find the one containing the sample we're after. + drflac_uint64 runningSampleCount = runningGranulePosition; + for (;;) { + // There are two ways to find the sample and seek past irrelevant frames: + // 1) Use the native FLAC decoder. + // 2) Use Ogg's framing system. + // + // Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to + // do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code + // duplication for the decoding of frame headers. + // + // Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg + // bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the + // standard drflac__*() APIs because that will read in extra data for it's own internal caching which in turn breaks + // the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read + // using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to + // avoid the use of the drflac_bs object. + // + // Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: + // 1) Seeking is already partially accellerated using Ogg's paging system in the code block above. + // 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. + // 3) Simplicity. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // it never existed and keep iterating. + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. + if (samplesToDecode == 0) { + return DRFLAC_TRUE; + } + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + // frame never existed and leave the running sample count untouched. + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } + } +} + + +drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +{ + // Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern. + (void)relaxed; + + pInit->container = drflac_container_ogg; + pInit->oggFirstBytePos = 0; + + // We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the + // stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if + // any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. + drflac_ogg_page_header header; + + drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + drflac_uint32 bytesRead = 0; + if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + pInit->runningFilePos += bytesRead; + + for (;;) { + // Break if we're past the beginning of stream page. + if ((header.headerType & 0x02) == 0) { + return DRFLAC_FALSE; + } + + + // Check if it's a FLAC header. + int pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize == 51) { // 51 = the lacing value of the FLAC header packet. + // It could be a FLAC page... + drflac_uint32 bytesRemainingInPage = pageBodySize; + + drflac_uint8 packetType; + if (onRead(pUserData, &packetType, 1) != 1) { + return DRFLAC_FALSE; + } + + bytesRemainingInPage -= 1; + if (packetType == 0x7F) { + // Increasingly more likely to be a FLAC page... + drflac_uint8 sig[4]; + if (onRead(pUserData, sig, 4) != 4) { + return DRFLAC_FALSE; + } + + bytesRemainingInPage -= 4; + if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { + // Almost certainly a FLAC page... + drflac_uint8 mappingVersion[2]; + if (onRead(pUserData, mappingVersion, 2) != 2) { + return DRFLAC_FALSE; + } + + if (mappingVersion[0] != 1) { + return DRFLAC_FALSE; // Only supporting version 1.x of the Ogg mapping. + } + + // The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to + // be handling it in a generic way based on the serial number and packet types. + if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + + // Expecting the native FLAC signature "fLaC". + if (onRead(pUserData, sig, 4) != 4) { + return DRFLAC_FALSE; + } + + if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { + // The remaining data in the page should be the STREAMINFO block. + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return DRFLAC_FALSE; // Invalid block type. First block must be the STREAMINFO block. + } + + drflac_streaminfo streaminfo; + if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + // Success! + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalSampleCount = streaminfo.totalSampleCount; + pInit->maxBlockSize = streaminfo.maxBlockSize; + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + + pInit->runningFilePos += pageBodySize; + pInit->oggFirstBytePos = pInit->runningFilePos - 79; // Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page. + pInit->oggSerial = header.serialNumber; + pInit->oggBosHeader = header; + break; + } else { + // Failed to read STREAMINFO block. Aww, so close... + return DRFLAC_FALSE; + } + } else { + // Invalid file. + return DRFLAC_FALSE; + } + } else { + // Not a FLAC header. Skip it. + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + // Not a FLAC header. Seek past the entire page and move on to the next. + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + + pInit->runningFilePos += pageBodySize; + + + // Read the header of the next page. + if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + pInit->runningFilePos += bytesRead; + } + + + // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next + // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialiation phase for Ogg is to create the + // Ogg bistream object. + pInit->hasMetadataBlocks = DRFLAC_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block. + return DRFLAC_TRUE; +} +#endif + +drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) +{ + if (pInit == NULL || onRead == NULL || onSeek == NULL) { + return DRFLAC_FALSE; + } + + drflac_zero_memory(pInit, sizeof(*pInit)); + pInit->onRead = onRead; + pInit->onSeek = onSeek; + pInit->onMeta = onMeta; + pInit->container = container; + pInit->pUserData = pUserData; + pInit->pUserDataMD = pUserDataMD; + + pInit->bs.onRead = onRead; + pInit->bs.onSeek = onSeek; + pInit->bs.pUserData = pUserData; + drflac__reset_cache(&pInit->bs); + + + // If the container is explicitly defined then we can try opening in relaxed mode. + drflac_bool32 relaxed = container != drflac_container_unknown; + + drflac_uint8 id[4]; + + // Skip over any ID3 tags. + for (;;) { + if (onRead(pUserData, id, 4) != 4) { + return DRFLAC_FALSE; // Ran out of data. + } + pInit->runningFilePos += 4; + + if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { + drflac_uint8 header[6]; + if (onRead(pUserData, header, 6) != 6) { + return DRFLAC_FALSE; // Ran out of data. + } + pInit->runningFilePos += 6; + + drflac_uint8 flags = header[1]; + drflac_uint32 headerSize; + drflac_copy_memory(&headerSize, header+2, 4); + headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); + if (flags & 0x10) { + headerSize += 10; + } + + if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; // Failed to seek past the tag. + } + pInit->runningFilePos += headerSize; + } else { + break; + } + } + + if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef DR_FLAC_NO_OGG + if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + + // If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. + if (relaxed) { + if (container == drflac_container_native) { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef DR_FLAC_NO_OGG + if (container == drflac_container_ogg) { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + } + + // Unsupported container. + return DRFLAC_FALSE; +} + +void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit) +{ + drflac_assert(pFlac != NULL); + drflac_assert(pInit != NULL); + + drflac_zero_memory(pFlac, sizeof(*pFlac)); + pFlac->bs = pInit->bs; + pFlac->onMeta = pInit->onMeta; + pFlac->pUserDataMD = pInit->pUserDataMD; + pFlac->maxBlockSize = pInit->maxBlockSize; + pFlac->sampleRate = pInit->sampleRate; + pFlac->channels = (drflac_uint8)pInit->channels; + pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; + pFlac->totalSampleCount = pInit->totalSampleCount; + pFlac->container = pInit->container; +} + +drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) +{ +#ifndef DRFLAC_NO_CPUID + // CPU support first. + drflac__init_cpu_caps(); +#endif + + drflac_init_info init; + if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + return NULL; + } + + // The size of the allocation for the drflac object needs to be large enough to fit the following: + // 1) The main members of the drflac structure + // 2) A block of memory large enough to store the decoded samples of the largest frame in the stream + // 3) If the container is Ogg, a drflac_oggbs object + // + // The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration + // the different SIMD instruction sets. + drflac_uint32 allocationSize = sizeof(drflac); + + // The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector + // we are supporting. + drflac_uint32 wholeSIMDVectorCountPerChannel; + if ((init.maxBlockSize % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); + } else { + wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; + } + + drflac_uint32 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + + allocationSize += decodedSamplesAllocationSize; + allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; // Allocate extra bytes to ensure we have enough for alignment. + +#ifndef DR_FLAC_NO_OGG + // There's additional data required for Ogg streams. + if (init.container == drflac_container_ogg) { + allocationSize += sizeof(drflac_oggbs); + } +#endif + + drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize); + drflac__init_from_info(pFlac, &init); + pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + drflac_oggbs* oggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + oggbs->onRead = onRead; + oggbs->onSeek = onSeek; + oggbs->pUserData = pUserData; + oggbs->currentBytePos = init.oggFirstBytePos; + oggbs->firstBytePos = init.oggFirstBytePos; + oggbs->serialNumber = init.oggSerial; + oggbs->bosPageHeader = init.oggBosHeader; + oggbs->bytesRemainingInPage = 0; + + // The Ogg bistream needs to be layered on top of the original bitstream. + pFlac->bs.onRead = drflac__on_read_ogg; + pFlac->bs.onSeek = drflac__on_seek_ogg; + pFlac->bs.pUserData = (void*)oggbs; + pFlac->_oggbs = (void*)oggbs; + } +#endif + + // Decode metadata before returning. + if (init.hasMetadataBlocks) { + if (!drflac__read_and_decode_metadata(pFlac)) { + DRFLAC_FREE(pFlac); + return NULL; + } + } + + // If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode + // the first frame. + if (!init.hasStreamInfoBlock) { + pFlac->currentFrame.header = init.firstFrameHeader; + do + { + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + break; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + DRFLAC_FREE(pFlac); + return NULL; + } + continue; + } else { + DRFLAC_FREE(pFlac); + return NULL; + } + } + } while (1); + } + + return pFlac; +} + + + +#ifndef DR_FLAC_NO_STDIO +typedef void* drflac_file; + +#if defined(DR_FLAC_NO_WIN32_IO) || !defined(_WIN32) +#include + +static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +static drflac_file drflac__open_file_handle(const char* filename) +{ + FILE* pFile; +#ifdef _MSC_VER + if (fopen_s(&pFile, filename, "rb") != 0) { + return NULL; + } +#else + pFile = fopen(filename, "rb"); + if (pFile == NULL) { + return NULL; + } +#endif + + return (drflac_file)pFile; +} + +static void drflac__close_file_handle(drflac_file file) +{ + fclose((FILE*)file); +} +#else +#include + +// This doesn't seem to be defined for VC6. +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac_assert(bytesToRead < 0xFFFFFFFF); // dr_flac will never request huge amounts of data at a time. This is a safe assertion. + + DWORD bytesRead; + ReadFile((HANDLE)pUserData, bufferOut, (DWORD)bytesToRead, &bytesRead, NULL); + + return (size_t)bytesRead; +} + +static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + return SetFilePointer((HANDLE)pUserData, offset, NULL, (origin == drflac_seek_origin_current) ? FILE_CURRENT : FILE_BEGIN) != INVALID_SET_FILE_POINTER; +} + +static drflac_file drflac__open_file_handle(const char* filename) +{ + HANDLE hFile = CreateFileA(filename, FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return NULL; + } + + return (drflac_file)hFile; +} + +static void drflac__close_file_handle(drflac_file file) +{ + CloseHandle((HANDLE)file); +} +#endif + + +drflac* drflac_open_file(const char* filename) +{ + drflac_file file = drflac__open_file_handle(filename); + if (file == NULL) { + return NULL; + } + + drflac* pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)file); + if (pFlac == NULL) { + drflac__close_file_handle(file); + return NULL; + } + + return pFlac; +} + +drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData) +{ + drflac_file file = drflac__open_file_handle(filename); + if (file == NULL) { + return NULL; + } + + drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)file, pUserData); + if (pFlac == NULL) { + drflac__close_file_handle(file); + return pFlac; + } + + return pFlac; +} +#endif //DR_FLAC_NO_STDIO + +static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + drflac_assert(memoryStream != NULL); + drflac_assert(memoryStream->dataSize >= memoryStream->currentReadPos); + + size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + drflac_copy_memory(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + memoryStream->currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + drflac_assert(memoryStream != NULL); + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + if (origin == drflac_seek_origin_current) { + if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { + memoryStream->currentReadPos += offset; + } else { + memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. + } + } else { + if ((drflac_uint32)offset <= memoryStream->dataSize) { + memoryStream->currentReadPos = offset; + } else { + memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. + } + } + + return DRFLAC_TRUE; +} + +drflac* drflac_open_memory(const void* data, size_t dataSize) +{ + drflac__memory_stream memoryStream; + memoryStream.data = (const unsigned char*)data; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + drflac* pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + // This is an awful hack... +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + +drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData) +{ + drflac__memory_stream memoryStream; + memoryStream.data = (const unsigned char*)data; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + // This is an awful hack... +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + + + +drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData); +} +drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData); +} + +drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData); +} +drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData); +} + +void drflac_close(drflac* pFlac) +{ + if (pFlac == NULL) { + return; + } + +#ifndef DR_FLAC_NO_STDIO + // If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file() + // was used by looking at the callbacks. + if (pFlac->bs.onRead == drflac__on_read_stdio) { + drflac__close_file_handle((drflac_file)pFlac->bs.pUserData); + } + +#ifndef DR_FLAC_NO_OGG + // Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. + if (pFlac->container == drflac_container_ogg) { + drflac_assert(pFlac->bs.onRead == drflac__on_read_ogg); + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + if (oggbs->onRead == drflac__on_read_stdio) { + drflac__close_file_handle((drflac_file)oggbs->pUserData); + } + } +#endif +#endif + + DRFLAC_FREE(pFlac); +} + +drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) +{ + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + + // We should never be calling this when the number of samples to read is >= the sample count. + drflac_assert(samplesToRead < channelCount); + drflac_assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining); + + + drflac_uint64 samplesRead = 0; + while (samplesToRead > 0) { + drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + drflac_uint64 channelIndex = samplesReadFromFrameSoFar % channelCount; + + drflac_uint64 nextSampleInFrame = samplesReadFromFrameSoFar / channelCount; + + int decodedSample = 0; + switch (pFlac->currentFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + if (channelIndex == 0) { + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + } else { + int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; + decodedSample = left - side; + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + if (channelIndex == 0) { + int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; + decodedSample = side + right; + } else { + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + int mid; + int side; + if (channelIndex == 0) { + mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; + + mid = (((unsigned int)mid) << 1) | (side & 0x01); + decodedSample = (mid + side) >> 1; + } else { + mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; + side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + + mid = (((unsigned int)mid) << 1) | (side & 0x01); + decodedSample = (mid - side) >> 1; + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + } break; + } + + + decodedSample <<= ((32 - pFlac->bitsPerSample) + pFlac->currentFrame.subframes[channelIndex].wastedBitsPerSample); + + if (bufferOut) { + *bufferOut++ = decodedSample; + } + + samplesRead += 1; + pFlac->currentFrame.samplesRemaining -= 1; + samplesToRead -= 1; + } + + return samplesRead; +} + +drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 samplesToRead) +{ + drflac_uint64 samplesRead = 0; + while (samplesToRead > 0) { + if (pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_and_decode_next_frame(pFlac)) { + break; // Couldn't read the next frame, so just break from the loop and return. + } + } else { + samplesRead += 1; + pFlac->currentFrame.samplesRemaining -= 1; + samplesToRead -= 1; + } + } + + return samplesRead; +} + +drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) +{ + // Note that is allowed to be null, in which case this will be treated as something like a seek. + if (pFlac == NULL || samplesToRead == 0) { + return 0; + } + + if (bufferOut == NULL) { + return drflac__seek_forward_by_samples(pFlac, samplesToRead); + } + + + drflac_uint64 samplesRead = 0; + while (samplesToRead > 0) { + // If we've run out of samples in this frame, go to the next. + if (pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_and_decode_next_frame(pFlac)) { + break; // Couldn't read the next frame, so just break from the loop and return. + } + } else { + // Here is where we grab the samples and interleave them. + + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + + drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; + if (misalignedSampleCount > 0) { + drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); + samplesRead += misalignedSamplesRead; + samplesReadFromFrameSoFar += misalignedSamplesRead; + bufferOut += misalignedSamplesRead; + samplesToRead -= misalignedSamplesRead; + } + + + drflac_uint64 alignedSampleCountPerChannel = samplesToRead / channelCount; + if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) { + alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount; + } + + drflac_uint64 firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount; + unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample; + + switch (pFlac->currentFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + int left = pDecodedSamples0[i]; + int side = pDecodedSamples1[i]; + int right = left - side; + + bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + int side = pDecodedSamples0[i]; + int right = pDecodedSamples1[i]; + int left = right + side; + + bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + int side = pDecodedSamples1[i]; + int mid = (((drflac_uint32)pDecodedSamples0[i]) << 1) | (side & 0x01); + + bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + if (pFlac->currentFrame.header.channelAssignment == 1) // 1 = Stereo + { + // Stereo optimized inner loop unroll. + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } + else + { + // Generic interleaving. + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + for (unsigned int j = 0; j < channelCount; ++j) { + bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample); + } + } + } + } break; + } + + drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount; + samplesRead += alignedSamplesRead; + samplesReadFromFrameSoFar += alignedSamplesRead; + bufferOut += alignedSamplesRead; + samplesToRead -= alignedSamplesRead; + pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead; + + + + // At this point we may still have some excess samples left to read. + if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) { + drflac_uint64 excessSamplesRead = 0; + if (samplesToRead < pFlac->currentFrame.samplesRemaining) { + excessSamplesRead = drflac__read_s32__misaligned(pFlac, samplesToRead, bufferOut); + } else { + excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut); + } + + samplesRead += excessSamplesRead; + samplesReadFromFrameSoFar += excessSamplesRead; + bufferOut += excessSamplesRead; + samplesToRead -= excessSamplesRead; + } + } + } + + return samplesRead; +} + +drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut) +{ + // This reads samples in 2 passes and can probably be optimized. + drflac_uint64 totalSamplesRead = 0; + + while (samplesToRead > 0) { + drflac_int32 samples32[4096]; + drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32); + if (samplesJustRead == 0) { + break; // Reached the end. + } + + // s32 -> s16 + for (drflac_uint64 i = 0; i < samplesJustRead; ++i) { + pBufferOut[i] = (drflac_int16)(samples32[i] >> 16); + } + + totalSamplesRead += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; + } + + return totalSamplesRead; +} + +drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut) +{ + // This reads samples in 2 passes and can probably be optimized. + drflac_uint64 totalSamplesRead = 0; + + while (samplesToRead > 0) { + drflac_int32 samples32[4096]; + drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32); + if (samplesJustRead == 0) { + break; // Reached the end. + } + + // s32 -> f32 + for (drflac_uint64 i = 0; i < samplesJustRead; ++i) { + pBufferOut[i] = (float)(samples32[i] / 2147483648.0); + } + + totalSamplesRead += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; + } + + return totalSamplesRead; +} + +drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) +{ + if (pFlac == NULL) { + return DRFLAC_FALSE; + } + + // If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present + // when the decoder was opened. + if (pFlac->firstFramePos == 0) { + return DRFLAC_FALSE; + } + + if (sampleIndex == 0) { + return drflac__seek_to_first_frame(pFlac); + } + + // Clamp the sample to the end. + if (sampleIndex >= pFlac->totalSampleCount) { + sampleIndex = pFlac->totalSampleCount - 1; + } + + + // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so + // we'll instead use Ogg's natural seeking facility. +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + return drflac_ogg__seek_to_sample(pFlac, sampleIndex); + } + else +#endif + { + // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. + if (!drflac__seek_to_sample__seek_table(pFlac, sampleIndex)) { + return drflac__seek_to_sample__brute_force(pFlac, sampleIndex); + } + } + + + return DRFLAC_TRUE; +} + + + +/* High Level APIs */ + +/* I couldn't figure out where SIZE_MAX was defined for VC6. If anybody knows, let me know. */ +#if defined(_MSC_VER) && _MSC_VER <= 1200 +#ifdef DRFLAC_64BIT +#define SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) +#else +#define SIZE_MAX 0xFFFFFFFF +#endif +#endif + +/* Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me. */ +#define DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(extension, type) \ +static type* drflac__full_decode_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)\ +{ \ + drflac_assert(pFlac != NULL); \ + \ + type* pSampleData = NULL; \ + drflac_uint64 totalSampleCount = pFlac->totalSampleCount; \ + \ + if (totalSampleCount == 0) { \ + type buffer[4096]; \ + \ + size_t sampleDataBufferSize = sizeof(buffer); \ + pSampleData = (type*)DRFLAC_MALLOC(sampleDataBufferSize); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + drflac_uint64 samplesRead; \ + while ((samplesRead = (drflac_uint64)drflac_read_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) { \ + if (((totalSampleCount + samplesRead) * sizeof(type)) > sampleDataBufferSize) { \ + sampleDataBufferSize *= 2; \ + type* pNewSampleData = (type*)DRFLAC_REALLOC(pSampleData, sampleDataBufferSize); \ + if (pNewSampleData == NULL) { \ + DRFLAC_FREE(pSampleData); \ + goto on_error; \ + } \ + \ + pSampleData = pNewSampleData; \ + } \ + \ + drflac_copy_memory(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(type))); \ + totalSampleCount += samplesRead; \ + } \ + \ + /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ + protect those ears from random noise! */ \ + drflac_zero_memory(pSampleData + totalSampleCount, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(type))); \ + } else { \ + drflac_uint64 dataSize = totalSampleCount * sizeof(type); \ + if (dataSize > SIZE_MAX) { \ + goto on_error; /* The decoded data is too big. */ \ + } \ + \ + pSampleData = (type*)DRFLAC_MALLOC((size_t)dataSize); /* <-- Safe cast as per the check above. */ \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + totalSampleCount = drflac_read_##extension(pFlac, pFlac->totalSampleCount, pSampleData); \ + } \ + \ + if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ + if (channelsOut) *channelsOut = pFlac->channels; \ + if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount; \ + \ + drflac_close(pFlac); \ + return pSampleData; \ + \ +on_error: \ + drflac_close(pFlac); \ + return NULL; \ +} + +DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s32, drflac_int32) +DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s16, drflac_int16) +DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(f32, float) + +drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + /* Safety. */ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); +} + +drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + /* Safety. */ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); +} + +float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + /* Safety. */ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); +} + +#ifndef DR_FLAC_NO_STDIO +drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_file(filename); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); +} + +drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_file(filename); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); +} + +float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_file(filename); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); +} +#endif + +drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); +} + +drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); +} + +float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); +} + +void drflac_free(void* pSampleDataReturnedByOpenAndDecode) +{ + DRFLAC_FREE(pSampleDataReturnedByOpenAndDecode); +} + + + + +void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments) +{ + if (pIter == NULL) { + return; + } + + pIter->countRemaining = commentCount; + pIter->pRunningData = pComments; +} + +const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) +{ + /* Safety. */ + if (pCommentLengthOut) *pCommentLengthOut = 0; + + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return NULL; + } + + drflac_uint32 length = drflac__le2host_32(*(drflac_uint32*)pIter->pRunningData); + pIter->pRunningData += 4; + + const char* pComment = pIter->pRunningData; + pIter->pRunningData += length; + pIter->countRemaining -= 1; + + if (pCommentLengthOut) *pCommentLengthOut = length; + return pComment; +} +#endif /* DR_FLAC_IMPLEMENTATION */ + +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to +*/ diff --git a/libretro-common/audio/audio_mixer.c b/libretro-common/audio/audio_mixer.c index b5bc82d906..b5f83f2e5c 100644 --- a/libretro-common/audio/audio_mixer.c +++ b/libretro-common/audio/audio_mixer.c @@ -585,7 +585,7 @@ error: } #endif -#ifdef HAV_DR_FLAC +#ifdef HAVE_DR_FLAC static bool audio_mixer_play_flac( audio_mixer_sound_t* sound, audio_mixer_voice_t* voice, diff --git a/libretro-common/include/audio/audio_mixer.h b/libretro-common/include/audio/audio_mixer.h index fa21b02e21..1541c431b7 100644 --- a/libretro-common/include/audio/audio_mixer.h +++ b/libretro-common/include/audio/audio_mixer.h @@ -61,6 +61,7 @@ void audio_mixer_done(void); audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size); audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size); +audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size); audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size); audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size); From 71c031099b3c39ba1f800889b6fcc9d8c955adb5 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Fri, 27 Apr 2018 22:44:53 -0400 Subject: [PATCH 501/517] Fix missing division in mali fbdev --- gfx/drivers_context/mali_fbdev_ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/drivers_context/mali_fbdev_ctx.c b/gfx/drivers_context/mali_fbdev_ctx.c index 8e88fbbcc7..3ffd90aedf 100644 --- a/gfx/drivers_context/mali_fbdev_ctx.c +++ b/gfx/drivers_context/mali_fbdev_ctx.c @@ -180,7 +180,7 @@ static bool gfx_ctx_mali_fbdev_set_video_mode(void *data, mali->native_window.height = vinfo.yres; mali->refresh_rate = 1000000.0f / vinfo.pixclock * 1000000.0f / - (vinfo.yres + vinfo.upper_margin + vinfo.lower_margin + vinfo.vsync_len) + (vinfo.yres + vinfo.upper_margin + vinfo.lower_margin + vinfo.vsync_len) / (vinfo.xres + vinfo.left_margin + vinfo.right_margin + vinfo.hsync_len); #ifdef HAVE_EGL From 3f7c318e3ffe63472b5e81c1f8effdea6dd2efa2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 11:17:37 +0200 Subject: [PATCH 502/517] (Audio mixer) Add FLAC support to audio mixer --- Makefile.common | 2 -- file_path_special.h | 1 + file_path_str.c | 3 +++ tasks/task_audio_mixer.c | 28 ++++++++++++++++++++++++++++ tasks/task_file_transfer.c | 1 + tasks/tasks_internal.h | 1 + 6 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Makefile.common b/Makefile.common index 59dd0dc7fd..8715b2d5ae 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1437,10 +1437,8 @@ endif ifeq ($(HAVE_BUILTINFLAC),1) HAVE_FLAC = 1 -ifneq ($(C89_BUILD), 1) DEFINES += -DHAVE_DR_FLAC -I$(DEPS_DIR) CFLAGS += -DHAVE_DR_FLAC -endif CFLAGS += -DHAVE_FLAC -I$(DEPS_DIR)/libFLAC/include DEFINES += -DHAVE_STDINT_H -DHAVE_LROUND -DFLAC__HAS_OGG=0 \ -DFLAC_PACKAGE_VERSION="\"retroarch\"" diff --git a/file_path_special.h b/file_path_special.h index b0eeee3bdc..a1b138c7d2 100644 --- a/file_path_special.h +++ b/file_path_special.h @@ -86,6 +86,7 @@ enum file_path_enum FILE_PATH_ZIP_EXTENSION, FILE_PATH_7Z_EXTENSION, FILE_PATH_OGG_EXTENSION, + FILE_PATH_FLAC_EXTENSION, FILE_PATH_WAV_EXTENSION, FILE_PATH_MOD_EXTENSION, FILE_PATH_S3M_EXTENSION, diff --git a/file_path_str.c b/file_path_str.c index 219c719720..d741109c56 100644 --- a/file_path_str.c +++ b/file_path_str.c @@ -101,6 +101,9 @@ const char *file_path_str(enum file_path_enum enum_idx) case FILE_PATH_PNG_EXTENSION: str = ".png"; break; + case FILE_PATH_FLAC_EXTENSION: + str = ".flac"; + break; case FILE_PATH_OGG_EXTENSION: str = ".ogg"; break; diff --git a/tasks/task_audio_mixer.c b/tasks/task_audio_mixer.c index 4acc62f3f6..0331d8dbcf 100644 --- a/tasks/task_audio_mixer.c +++ b/tasks/task_audio_mixer.c @@ -102,6 +102,28 @@ static void task_audio_mixer_handle_upload_ogg(void *task_data, free(user_data); } +static void task_audio_mixer_handle_upload_flac(void *task_data, + void *user_data, const char *err) +{ + audio_mixer_stream_params_t params; + nbio_buf_t *img = (nbio_buf_t*)task_data; + + if (!img) + return; + + params.volume = 1.0f; + params.type = AUDIO_MIXER_TYPE_FLAC; + params.state = AUDIO_STREAM_STATE_PLAYING; + params.buf = img->buf; + params.bufsize = img->bufsize; + params.cb = NULL; + + audio_driver_mixer_add_stream(¶ms); + + free(img); + free(user_data); +} + static void task_audio_mixer_handle_upload_mod(void *task_data, void *user_data, const char *err) { @@ -216,6 +238,12 @@ bool task_push_audio_mixer_load(const char *fullpath, retro_task_callback_t cb, nbio->type = NBIO_TYPE_OGG; t->callback = task_audio_mixer_handle_upload_ogg; } + else if (strstr(fullpath, file_path_str(FILE_PATH_FLAC_EXTENSION))) + { + image->type = AUDIO_MIXER_TYPE_FLAC; + nbio->type = NBIO_TYPE_FLAC; + t->callback = task_audio_mixer_handle_upload_flac; + } else if ( strstr(fullpath, file_path_str(FILE_PATH_MOD_EXTENSION)) || strstr(fullpath, file_path_str(FILE_PATH_S3M_EXTENSION)) || strstr(fullpath, file_path_str(FILE_PATH_XM_EXTENSION))) diff --git a/tasks/task_file_transfer.c b/tasks/task_file_transfer.c index 9d8ed3976a..849c712267 100644 --- a/tasks/task_file_transfer.c +++ b/tasks/task_file_transfer.c @@ -103,6 +103,7 @@ void task_file_load_handler(retro_task_t *task) if (!task_image_load_handler(task)) task_set_finished(task, true); break; + case NBIO_TYPE_FLAC: case NBIO_TYPE_OGG: case NBIO_TYPE_MOD: case NBIO_TYPE_WAV: diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index 4e8e4e5a9f..426e3fbf31 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -73,6 +73,7 @@ enum nbio_type NBIO_TYPE_TGA, NBIO_TYPE_BMP, NBIO_TYPE_OGG, + NBIO_TYPE_FLAC, NBIO_TYPE_MOD, NBIO_TYPE_WAV }; From 437c4fbc0a9296631c532b25cdc6e608c5b99988 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 12:37:50 +0200 Subject: [PATCH 503/517] (dr_flac.h) C89_BUILD fixes --- deps/dr/dr_flac.h | 4508 ++++++++++++++++++++++----------------------- 1 file changed, 2242 insertions(+), 2266 deletions(-) diff --git a/deps/dr/dr_flac.h b/deps/dr/dr_flac.h index 4789d1a022..6e3ac33022 100644 --- a/deps/dr/dr_flac.h +++ b/deps/dr/dr_flac.h @@ -2,6 +2,7 @@ #define dr_flac_h #include +#include #if defined(_MSC_VER) && _MSC_VER < 1600 typedef signed char drflac_int8; @@ -254,376 +255,386 @@ typedef struct void* pUserData; - // The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the - // stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether - // or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t). + /* The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the + * stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether + * or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t). + */ size_t unalignedByteCount; - // The content of the unaligned bytes. + /* The content of the unaligned bytes. */ drflac_cache_t unalignedCache; - // The index of the next valid cache line in the "L2" cache. + /* The index of the next valid cache line in the "L2" cache. */ drflac_uint32 nextL2Line; - // The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. + /* The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. */ drflac_uint32 consumedBits; - // The cached data which was most recently read from the client. There are two levels of cache. Data flows as such: - // Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions. + /* The cached data which was most recently read from the client. There are two levels of cache. Data flows as such: + * Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions. */ drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; drflac_cache_t cache; - // CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this - // is reset to 0 at the beginning of each frame. + /* CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this + * is reset to 0 at the beginning of each frame. */ drflac_uint16 crc16; - drflac_cache_t crc16Cache; // A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. - drflac_uint32 crc16CacheIgnoredBytes; // The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. + drflac_cache_t crc16Cache; /* A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. */ + drflac_uint32 crc16CacheIgnoredBytes; /* The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. */ } drflac_bs; typedef struct { - // The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. + /* The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. */ drflac_uint8 subframeType; - // The number of wasted bits per sample as specified by the sub-frame header. + /* The number of wasted bits per sample as specified by the sub-frame header. */ drflac_uint8 wastedBitsPerSample; - // The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. + /* The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. */ drflac_uint8 lpcOrder; - // The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because - // an extra bit is required for side channels when interchannel decorrelation is being used. + /* The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because + * an extra bit is required for side channels when interchannel decorrelation is being used. */ drflac_uint32 bitsPerSample; - // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. Note that - // it's a signed 32-bit integer for each value. + /* A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. Note that + * it's a signed 32-bit integer for each value. */ drflac_int32* pDecodedSamples; } drflac_subframe; typedef struct { - // If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will - // always be set to 0. + /* If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will + * always be set to 0. */ drflac_uint64 sampleNumber; - // If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. + /* If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. */ drflac_uint32 frameNumber; - // The sample rate of this frame. + /* The sample rate of this frame. */ drflac_uint32 sampleRate; - // The number of samples in each sub-frame within this frame. + /* The number of samples in each sub-frame within this frame. */ drflac_uint16 blockSize; - // The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this - // will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. + /* The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this + * will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. */ drflac_uint8 channelAssignment; - // The number of bits per sample within this frame. + /* The number of bits per sample within this frame. */ drflac_uint8 bitsPerSample; - // The frame's CRC. + /* The frame's CRC. */ drflac_uint8 crc8; } drflac_frame_header; typedef struct { - // The header. + /* The header. */ drflac_frame_header header; - // The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples - // are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. + /* The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples + * are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. + */ drflac_uint32 samplesRemaining; - // The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. + /* The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. */ drflac_subframe subframes[8]; } drflac_frame; typedef struct { - // The function to call when a metadata block is read. + /* The function to call when a metadata block is read. */ drflac_meta_proc onMeta; - // The user data posted to the metadata callback function. + /* The user data posted to the metadata callback function. */ void* pUserDataMD; - // The sample rate. Will be set to something like 44100. + /* The sample rate. Will be set to something like 44100. */ drflac_uint32 sampleRate; - // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the - // value specified in the STREAMINFO block. + /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the + * value specified in the STREAMINFO block. */ drflac_uint8 channels; - // The bits per sample. Will be set to somthing like 16, 24, etc. + /* The bits per sample. Will be set to somthing like 16, 24, etc. */ drflac_uint8 bitsPerSample; - // The maximum block size, in samples. This number represents the number of samples in each channel (not combined). + /* The maximum block size, in samples. This number represents the number of samples in each channel (not combined). */ drflac_uint16 maxBlockSize; - // The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels, - // with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a - // valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio. + /* The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels, + * with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a + * valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio. */ drflac_uint64 totalSampleCount; - // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. + /* The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. */ drflac_container container; - - // The position of the seektable in the file. + /* The position of the seektable in the file. */ drflac_uint64 seektablePos; - // The size of the seektable. + /* The size of the seektable. */ drflac_uint32 seektableSize; - // Information about the frame the decoder is currently sitting on. + /* Information about the frame the decoder is currently sitting on. */ drflac_frame currentFrame; - // The position of the first frame in the stream. This is only ever used for seeking. + /* The position of the first frame in the stream. This is only ever used for seeking. */ drflac_uint64 firstFramePos; - // A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). + /* A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). */ drflac__memory_stream memoryStream; - // A pointer to the decoded sample data. This is an offset of pExtraData. + /* A pointer to the decoded sample data. This is an offset of pExtraData. */ drflac_int32* pDecodedSamples; - // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. + /* Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. */ void* _oggbs; - // The bit streamer. The raw FLAC data is fed through this object. + /* The bit streamer. The raw FLAC data is fed through this object. */ drflac_bs bs; - // Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. + /* Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. */ drflac_uint8 pExtraData[1]; } drflac; -// Opens a FLAC decoder. -// -// onRead [in] The function to call when data needs to be read from the client. -// onSeek [in] The function to call when the read position of the client data needs to move. -// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. -// -// Returns a pointer to an object representing the decoder. -// -// Close the decoder with drflac_close(). -// -// This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated -// FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with -// multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. -// -// This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory() -// to open the stream from a file or from a block of memory respectively. -// -// The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where -// the header may not be present. -// -// See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close() +/* Opens a FLAC decoder. + * + * onRead [in] The function to call when data needs to be read from the client. + * onSeek [in] The function to call when the read position of the client data needs to move. + * pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. + * + * Returns a pointer to an object representing the decoder. + * + * Close the decoder with drflac_close(). + * + * This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated + * FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with + * multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. + * + * This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory() + * to open the stream from a file or from a block of memory respectively. + * + * The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where + * the header may not be present. + * + * See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close() + */ drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData); -// The same as drflac_open(), except attempts to open the stream even when a header block is not present. -// -// Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do -// not set this to drflac_container_unknown - that is for internal use only. -// -// Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never -// found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an -// indicator that the end of the stream was found. +/* The same as drflac_open(), except attempts to open the stream even when a header block is not present. + * + * Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do + * not set this to drflac_container_unknown - that is for internal use only. + * + * Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never + * found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an + * indicator that the end of the stream was found. */ drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData); -// Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). -// -// onRead [in] The function to call when data needs to be read from the client. -// onSeek [in] The function to call when the read position of the client data needs to move. -// onMeta [in] The function to call for every metadata block. -// pUserData [in, optional] A pointer to application defined data that will be passed to onRead, onSeek and onMeta. -// -// Returns a pointer to an object representing the decoder. -// -// Close the decoder with drflac_close(). -// -// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a DRFLAC_MALLOC() -// and DRFLAC_FREE() for every metadata block except for STREAMINFO and PADDING blocks. -// -// The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function -// returns. -// -// The STREAMINFO block must be present for this to succeed. Use drflac_open_with_metadata_relaxed() to open a FLAC -// stream where the header may not be present. -// -// Note that this will behave inconsistently with drflac_open() if the stream is an Ogg encapsulated stream and a metadata -// block is corrupted. This is due to the way the Ogg stream recovers from corrupted pages. When drflac_open_with_metadata() -// is being used, the open routine will try to read the contents of the metadata block, whereas drflac_open() will simply -// seek past it (for the sake of efficiency). This inconsistency can result in different samples being returned depending on -// whether or not the stream is being opened with metadata. -// -// See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close() +/* Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). + * + * onRead [in] The function to call when data needs to be read from the client. + * onSeek [in] The function to call when the read position of the client data needs to move. + * onMeta [in] The function to call for every metadata block. + * pUserData [in, optional] A pointer to application defined data that will be passed to onRead, onSeek and onMeta. + * + * Returns a pointer to an object representing the decoder. + * + * Close the decoder with drflac_close(). + * + * This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a DRFLAC_MALLOC() + * and DRFLAC_FREE() for every metadata block except for STREAMINFO and PADDING blocks. + * + * The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function + * returns. + * + * The STREAMINFO block must be present for this to succeed. Use drflac_open_with_metadata_relaxed() to open a FLAC + * stream where the header may not be present. + * + * Note that this will behave inconsistently with drflac_open() if the stream is an Ogg encapsulated stream and a metadata + * block is corrupted. This is due to the way the Ogg stream recovers from corrupted pages. When drflac_open_with_metadata() + * is being used, the open routine will try to read the contents of the metadata block, whereas drflac_open() will simply + * seek past it (for the sake of efficiency). This inconsistency can result in different samples being returned depending on + * whether or not the stream is being opened with metadata. + * + * See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close() + */ drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData); -// The same as drflac_open_with_metadata(), except attemps to open the stream even when a header block is not present. -// -// See also: drflac_open_with_metadata(), drflac_open_relaxed() +/* The same as drflac_open_with_metadata(), except attemps to open the stream even when a header block is not present. + * + * See also: drflac_open_with_metadata(), drflac_open_relaxed() + */ drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData); -// Closes the given FLAC decoder. -// -// pFlac [in] The decoder to close. -// -// This will destroy the decoder object. +/* Closes the given FLAC decoder. + * + * pFlac [in] The decoder to close. + * + * This will destroy the decoder object. */ void drflac_close(drflac* pFlac); -// Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. -// -// pFlac [in] The decoder. -// samplesToRead [in] The number of samples to read. -// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. -// -// Returns the number of samples actually read. -// -// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples -// seeked. +/* Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. + * + * pFlac [in] The decoder. + * samplesToRead [in] The number of samples to read. + * pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. + * + * Returns the number of samples actually read. + * + * pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples + * seeked. */ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* pBufferOut); -// Same as drflac_read_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit. -// -// pFlac [in] The decoder. -// samplesToRead [in] The number of samples to read. -// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. -// -// Returns the number of samples actually read. -// -// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples -// seeked. -// -// Note that this is lossy for streams where the bits per sample is larger than 16. +/* Same as drflac_read_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit. + * + * pFlac [in] The decoder. + * samplesToRead [in] The number of samples to read. + * pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. + * + * Returns the number of samples actually read. + * + * pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples + * seeked. + * + * Note that this is lossy for streams where the bits per sample is larger than 16. + */ drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut); -// Same as drflac_read_s32(), except outputs samples as 32-bit floating-point PCM. -// -// pFlac [in] The decoder. -// samplesToRead [in] The number of samples to read. -// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. -// -// Returns the number of samples actually read. -// -// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples -// seeked. -// -// Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly -// represent every possible number. +/* Same as drflac_read_s32(), except outputs samples as 32-bit floating-point PCM. + * + * pFlac [in] The decoder. + * samplesToRead [in] The number of samples to read. + * pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. + * + * Returns the number of samples actually read. + * + * pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples + * seeked. + * + * Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly + * represent every possible number. + */ drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut); -// Seeks to the sample at the given index. -// -// pFlac [in] The decoder. -// sampleIndex [in] The index of the sample to seek to. See notes below. -// -// Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise. -// -// The sample index is based on interleaving. In a stereo stream, for example, the sample at index 0 is the first sample -// in the left channel; the sample at index 1 is the first sample on the right channel, and so on. -// -// When seeking, you will likely want to ensure it's rounded to a multiple of the channel count. You can do this with -// something like drflac_seek_to_sample(pFlac, (mySampleIndex + (mySampleIndex % pFlac->channels))) +/* Seeks to the sample at the given index. + * + * pFlac [in] The decoder. + * sampleIndex [in] The index of the sample to seek to. See notes below. + * + * Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise. + * + * The sample index is based on interleaving. In a stereo stream, for example, the sample at index 0 is the first sample + * in the left channel; the sample at index 1 is the first sample on the right channel, and so on. + * + * When seeking, you will likely want to ensure it's rounded to a multiple of the channel count. You can do this with + * something like drflac_seek_to_sample(pFlac, (mySampleIndex + (mySampleIndex % pFlac->channels))) + */ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex); #ifndef DR_FLAC_NO_STDIO -// Opens a FLAC decoder from the file at the given path. -// -// filename [in] The path of the file to open, either absolute or relative to the current directory. -// -// Returns a pointer to an object representing the decoder. -// -// Close the decoder with drflac_close(). -// -// This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the -// number of files a process can have open at any given time, so keep this mind if you have many decoders open at the -// same time. -// -// See also: drflac_open(), drflac_open_file_with_metadata(), drflac_close() +/* Opens a FLAC decoder from the file at the given path. + * + * filename [in] The path of the file to open, either absolute or relative to the current directory. + * + * Returns a pointer to an object representing the decoder. + * + * Close the decoder with drflac_close(). + * + * This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the + * number of files a process can have open at any given time, so keep this mind if you have many decoders open at the + * same time. + * + * See also: drflac_open(), drflac_open_file_with_metadata(), drflac_close() + */ drflac* drflac_open_file(const char* filename); -// Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) -// -// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. +/* Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) + * + * Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. + */ drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData); #endif -// Opens a FLAC decoder from a pre-allocated block of memory -// -// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for -// the lifetime of the decoder. +/* Opens a FLAC decoder from a pre-allocated block of memory + * + * This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for + * the lifetime of the decoder. + */ drflac* drflac_open_memory(const void* data, size_t dataSize); -// Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) -// -// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. +/* Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) + * + * Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. + */ drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData); +/* High Level APIs */ - -//// High Level APIs //// - -// Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a -// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with DRFLAC_FREE(). -// -// Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously -// read samples into a dynamically sized buffer on the heap until no samples are left. -// -// Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). +/* Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a + * pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with DRFLAC_FREE(). + * + * Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously + * read samples into a dynamically sized buffer on the heap until no samples are left. + * + * Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). + */ drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); -// Same as drflac_open_and_decode_s32(), except returns signed 16-bit integer samples. +/* Same as drflac_open_and_decode_s32(), except returns signed 16-bit integer samples. */ drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); -// Same as drflac_open_and_decode_s32(), except returns 32-bit floating-point samples. +/* Same as drflac_open_and_decode_s32(), except returns 32-bit floating-point samples. */ float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); #ifndef DR_FLAC_NO_STDIO -// Same as drflac_open_and_decode_s32() except opens the decoder from a file. +/* Same as drflac_open_and_decode_s32() except opens the decoder from a file. */ drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); -// Same as drflac_open_and_decode_file_s32(), except returns signed 16-bit integer samples. +/* Same as drflac_open_and_decode_file_s32(), except returns signed 16-bit integer samples. */ drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); -// Same as drflac_open_and_decode_file_f32(), except returns 32-bit floating-point samples. +/* Same as drflac_open_and_decode_file_f32(), except returns 32-bit floating-point samples. */ float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); #endif -// Same as drflac_open_and_decode_s32() except opens the decoder from a block of memory. +/* Same as drflac_open_and_decode_s32() except opens the decoder from a block of memory. */ drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); -// Same as drflac_open_and_decode_memory_s32(), except returns signed 16-bit integer samples. +/* Same as drflac_open_and_decode_memory_s32(), except returns signed 16-bit integer samples. */ drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); -// Same as drflac_open_and_decode_memory_s32(), except returns 32-bit floating-point samples. +/* Same as drflac_open_and_decode_memory_s32(), except returns 32-bit floating-point samples. */ float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); -// Frees memory that was allocated internally by dr_flac. +/* Frees memory that was allocated internally by dr_flac. */ void drflac_free(void* p); -// Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. +/* Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. */ typedef struct { drflac_uint32 countRemaining; const char* pRunningData; } drflac_vorbis_comment_iterator; -// Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT -// metadata block. +/* Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT + * metadata block. */ void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments); -// Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The -// returned string is NOT null terminated. +/* Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The + * returned string is NOT null terminated. */ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); @@ -631,19 +642,19 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr #ifdef __cplusplus } #endif -#endif //dr_flac_h +#endif /* dr_flac_h */ -/////////////////////////////////////////////////////////////////////////////// -// -// IMPLEMENTATION -// -/////////////////////////////////////////////////////////////////////////////// +/* + * + * IMPLEMENTATION + * + */ #ifdef DR_FLAC_IMPLEMENTATION #include #include -// CPU architecture. +/* CPU architecture. */ #if defined(__x86_64__) || defined(_M_X64) #define DRFLAC_X64 #elif defined(__i386) || defined(_M_IX86) @@ -652,7 +663,7 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr #define DRFLAC_ARM #endif -// Compile-time CPU feature support. +/* Compile-time CPU feature support. */ #if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) #ifdef _MSC_VER #if _MSC_VER >= 1400 @@ -668,7 +679,7 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr #if defined(__GNUC__) || defined(__clang__) static void drflac__cpuid(int info[4], int fid) { - asm ( + __asm__ ( "movl %[fid], %%eax\n\t" "cpuid\n\t" "movl %%eax, %[info0]\n\t" @@ -717,8 +728,7 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr #endif #endif - -// Standard library stuff. +/* Standard library stuff. */ #ifndef DRFLAC_ASSERT #include #define DRFLAC_ASSERT(expression) assert(expression) @@ -739,21 +749,21 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr #define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif -#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future. +#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */ #ifdef _MSC_VER #define DRFLAC_INLINE __forceinline #else #ifdef __GNUC__ -#define DRFLAC_INLINE inline __attribute__((always_inline)) +#define DRFLAC_INLINE INLINE __attribute__((always_inline)) #else -#define DRFLAC_INLINE inline +#define DRFLAC_INLINE INLINE #endif #endif typedef drflac_int32 drflac_result; #define DRFLAC_SUCCESS 0 -#define DRFLAC_ERROR -1 // A generic error. +#define DRFLAC_ERROR -1 /* A generic error. */ #define DRFLAC_INVALID_ARGS -2 #define DRFLAC_END_OF_STREAM -128 #define DRFLAC_CRC_MISMATCH -129 @@ -778,8 +788,7 @@ typedef drflac_int32 drflac_result; #define drflac_copy_memory DRFLAC_COPY_MEMORY #define drflac_zero_memory DRFLAC_ZERO_MEMORY - -// CPU caps. +/* CPU caps. */ static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; #ifndef DRFLAC_NO_CPUID static drflac_bool32 drflac__gIsSSE42Supported = DRFLAC_FALSE; @@ -787,18 +796,18 @@ static void drflac__init_cpu_caps() { int info[4] = {0}; - // LZCNT + /* LZCNT */ drflac__cpuid(info, 0x80000001); drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; - // SSE4.2 + /* SSE4.2 */ drflac__cpuid(info, 1); drflac__gIsSSE42Supported = (info[2] & (1 << 19)) != 0; } #endif -//// Endian Management //// +/* Endian Management */ static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian() { #if defined(DRFLAC_X86) || defined(DRFLAC_X64) @@ -871,9 +880,8 @@ static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) #ifdef __linux__ return be16toh(n); #else - if (drflac__is_little_endian()) { + if (drflac__is_little_endian()) return drflac__swap_endian_uint16(n); - } return n; #endif @@ -884,9 +892,8 @@ static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) #ifdef __linux__ return be32toh(n); #else - if (drflac__is_little_endian()) { + if (drflac__is_little_endian()) return drflac__swap_endian_uint32(n); - } return n; #endif @@ -897,9 +904,8 @@ static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) #ifdef __linux__ return be64toh(n); #else - if (drflac__is_little_endian()) { + if (drflac__is_little_endian()) return drflac__swap_endian_uint64(n); - } return n; #endif @@ -911,9 +917,8 @@ static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) #ifdef __linux__ return le32toh(n); #else - if (!drflac__is_little_endian()) { + if (!drflac__is_little_endian()) return drflac__swap_endian_uint32(n); - } return n; #endif @@ -931,9 +936,7 @@ static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) return result; } - - -// The CRC code below is based on this document: http://zlib.net/crc_v3.txt +/* The CRC code below is based on this document: http://zlib.net/crc_v3.txt */ static drflac_uint8 drflac__crc8_table[] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, @@ -1004,15 +1007,15 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 da return 0; #else #if 0 - // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") + /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") */ drflac_uint8 p = 0x07; - for (int i = count-1; i >= 0; --i) { - drflac_uint8 bit = (data & (1 << i)) >> i; - if (crc & 0x80) { - crc = ((crc << 1) | bit) ^ p; - } else { - crc = ((crc << 1) | bit); - } + for (int i = count-1; i >= 0; --i) + { + drflac_uint8 bit = (data & (1 << i)) >> i; + if (crc & 0x80) + crc = ((crc << 1) | bit) ^ p; + else + crc = ((crc << 1) | bit); } return crc; #else @@ -1024,7 +1027,8 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 da }; drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { + switch (wholeBytes) + { case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); @@ -1071,15 +1075,15 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac return 0; #else #if 0 - // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") + /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") */ drflac_uint16 p = 0x8005; - for (int i = count-1; i >= 0; --i) { - drflac_uint16 bit = (data & (1ULL << i)) >> i; - if (r & 0x8000) { - r = ((r << 1) | bit) ^ p; - } else { - r = ((r << 1) | bit); - } + for (int i = count-1; i >= 0; --i) + { + drflac_uint16 bit = (data & (1ULL << i)) >> i; + if (r & 0x8000) + r = ((r << 1) | bit) ^ p; + else + r = ((r << 1) | bit); } return crc; @@ -1092,7 +1096,8 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac }; drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { + switch (wholeBytes) + { default: case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); @@ -1123,7 +1128,8 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac }; drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { + switch (wholeBytes) + { default: case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0xFF00000000000000 << leftoverBits)) >> (56 + leftoverBits))); case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00FF000000000000 << leftoverBits)) >> (48 + leftoverBits))); @@ -1156,13 +1162,14 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_ #define drflac__be2host__cache_line drflac__be2host_32 #endif -// BIT READING ATTEMPT #2 -// -// This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting -// on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache -// is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an -// array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data -// from onRead() is read into. +/* BIT READING ATTEMPT #2 + * + * This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting + * on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache + * is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an + * array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data + * from onRead() is read into. + */ #define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) #define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) #define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - ((bs)->consumedBits)) @@ -1194,19 +1201,20 @@ static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) { - // We should never be flushing in a situation where we are not aligned on a byte boundary. + /* We should never be flushing in a situation where we are not aligned on a byte boundary. */ drflac_assert((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); - // The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined - // by the number of bits that have been consumed. - if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + /* The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined + * by the number of bits that have been consumed. */ + if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) drflac__update_crc16(bs); - } else { - // We only accumulate the consumed bits. + else + { + /* We only accumulate the consumed bits. */ bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); - // The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated - // so we can handle that later. + /* The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated + * so we can handle that later. */ bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } @@ -1216,53 +1224,57 @@ static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) { - // Fast path. Try loading straight from L2. - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; - } + size_t alignedL1LineCount; + size_t bytesRead; + /* Fast path. Try loading straight from L2. */ + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) + { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } - // If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's - // any left. - if (bs->unalignedByteCount > 0) { - return DRFLAC_FALSE; // If we have any unaligned bytes it means there's no more aligned bytes left in the client. - } + /* If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's + * any left. */ + if (bs->unalignedByteCount > 0) + return DRFLAC_FALSE; /* If we have any unaligned bytes it means there's no more aligned bytes left in the client. */ - size_t bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); + bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); - bs->nextL2Line = 0; - if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; - } + bs->nextL2Line = 0; + if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) + { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } - // If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably - // means we've just reached the end of the file. We need to move the valid data down to the end of the buffer - // and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to - // the size of the L1 so we'll need to seek backwards by any misaligned bytes. - size_t alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); + /* If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably + * means we've just reached the end of the file. We need to move the valid data down to the end of the buffer + * and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to + * the size of the L1 so we'll need to seek backwards by any misaligned bytes. + */ + alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); - // We need to keep track of any unaligned bytes for later use. - bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); - if (bs->unalignedByteCount > 0) { - bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; - } + /* We need to keep track of any unaligned bytes for later use. */ + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + if (bs->unalignedByteCount > 0) + bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; - if (alignedL1LineCount > 0) { - size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; - for (size_t i = alignedL1LineCount; i > 0; --i) { - bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; - } + if (alignedL1LineCount > 0) + { + size_t i; + size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + for (i = alignedL1LineCount; i > 0; --i) + bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; - bs->nextL2Line = (drflac_uint32)offset; - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; - } else { - // If we get into this branch it means we weren't able to load any L1-aligned data. - bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); - return DRFLAC_FALSE; - } + bs->nextL2Line = (drflac_uint32)offset; + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } + + /* If we get into this branch it means we weren't able to load any L1-aligned data. */ + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); + return DRFLAC_FALSE; } static drflac_bool32 drflac__reload_cache(drflac_bs* bs) @@ -1271,8 +1283,9 @@ static drflac_bool32 drflac__reload_cache(drflac_bs* bs) drflac__update_crc16(bs); #endif - // Fast path. Try just moving the next value in the L2 cache to the L1 cache. - if (drflac__reload_l1_cache_from_l2(bs)) { + /* Fast path. Try just moving the next value in the L2 cache to the L1 cache. */ + if (drflac__reload_l1_cache_from_l2(bs)) + { bs->cache = drflac__be2host__cache_line(bs->cache); bs->consumedBits = 0; #ifndef DR_FLAC_NO_CRC @@ -1281,22 +1294,21 @@ static drflac_bool32 drflac__reload_cache(drflac_bs* bs) return DRFLAC_TRUE; } - // Slow path. + /* Slow path. */ - // If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last - // few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the - // data from the unaligned cache. + /* If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last + * few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the + * data from the unaligned cache. */ size_t bytesRead = bs->unalignedByteCount; - if (bytesRead == 0) { + if (bytesRead == 0) return DRFLAC_FALSE; - } drflac_assert(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; bs->cache = drflac__be2host__cache_line(bs->unalignedCache); - bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs->consumedBits); // <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. - bs->unalignedByteCount = 0; // <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. + bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs->consumedBits); /* <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. */ + bs->unalignedByteCount = 0; /* <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. */ #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache >> bs->consumedBits; @@ -1307,10 +1319,10 @@ static drflac_bool32 drflac__reload_cache(drflac_bs* bs) static void drflac__reset_cache(drflac_bs* bs) { - bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); // <-- This clears the L2 cache. - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); // <-- This clears the L1 cache. + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); /* <-- This clears the L2 cache. */ + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- This clears the L1 cache. */ bs->cache = 0; - bs->unalignedByteCount = 0; // <-- This clears the trailing unaligned bytes. + bs->unalignedByteCount = 0; /* <-- This clears the trailing unaligned bytes. */ bs->unalignedCache = 0; #ifndef DR_FLAC_NO_CRC @@ -1327,14 +1339,16 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i drflac_assert(bitCount > 0); drflac_assert(bitCount <= 32); - if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - if (!drflac__reload_cache(bs)) { + if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) + { + if (!drflac__reload_cache(bs)) return DRFLAC_FALSE; - } } - if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) + { + if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) + { *pResultOut = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; @@ -1345,14 +1359,13 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i } return DRFLAC_TRUE; } else { - // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. + /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); drflac_uint32 bitCountLo = bitCount - bitCountHi; drflac_uint32 resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); - if (!drflac__reload_cache(bs)) { + if (!drflac__reload_cache(bs)) return DRFLAC_FALSE; - } *pResultOut = (resultHi << bitCountLo) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; @@ -1369,9 +1382,8 @@ static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, dr drflac_assert(bitCount <= 32); drflac_uint32 result; - if (!drflac__read_uint32(bs, bitCount, &result)) { + if (!drflac__read_uint32(bs, bitCount, &result)) return DRFLAC_FALSE; - } drflac_uint32 signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; @@ -1382,33 +1394,31 @@ static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, dr static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) { + drflac_uint32 resultHi; + drflac_uint32 resultLo; + drflac_assert(bitCount <= 64); drflac_assert(bitCount > 32); - drflac_uint32 resultHi; - if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { + if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) return DRFLAC_FALSE; - } - drflac_uint32 resultLo; - if (!drflac__read_uint32(bs, 32, &resultLo)) { + if (!drflac__read_uint32(bs, 32, &resultLo)) return DRFLAC_FALSE; - } *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); return DRFLAC_TRUE; } -// Function below is unused, but leaving it here in case I need to quickly add it again. +/* Function below is unused, but leaving it here in case I need to quickly add it again. */ #if 0 static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) { drflac_assert(bitCount <= 64); drflac_uint64 result; - if (!drflac__read_uint64(bs, bitCount, &result)) { + if (!drflac__read_uint64(bs, bitCount, &result)) return DRFLAC_FALSE; - } drflac_uint64 signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; @@ -1420,15 +1430,15 @@ static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, dr static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) { + drflac_uint32 result; + drflac_assert(bs != NULL); drflac_assert(pResult != NULL); drflac_assert(bitCount > 0); drflac_assert(bitCount <= 16); - drflac_uint32 result; - if (!drflac__read_uint32(bs, bitCount, &result)) { + if (!drflac__read_uint32(bs, bitCount, &result)) return DRFLAC_FALSE; - } *pResult = (drflac_uint16)result; return DRFLAC_TRUE; @@ -1437,15 +1447,15 @@ static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, d #if 0 static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) { + drflac_int32 result; + drflac_assert(bs != NULL); drflac_assert(pResult != NULL); drflac_assert(bitCount > 0); drflac_assert(bitCount <= 16); - drflac_int32 result; - if (!drflac__read_int32(bs, bitCount, &result)) { + if (!drflac__read_int32(bs, bitCount, &result)) return DRFLAC_FALSE; - } *pResult = (drflac_int16)result; return DRFLAC_TRUE; @@ -1454,15 +1464,15 @@ static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, dr static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) { + drflac_uint32 result; + drflac_assert(bs != NULL); drflac_assert(pResult != NULL); drflac_assert(bitCount > 0); drflac_assert(bitCount <= 8); - drflac_uint32 result; - if (!drflac__read_uint32(bs, bitCount, &result)) { + if (!drflac__read_uint32(bs, bitCount, &result)) return DRFLAC_FALSE; - } *pResult = (drflac_uint8)result; return DRFLAC_TRUE; @@ -1470,15 +1480,15 @@ static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, dr static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) { + drflac_int32 result; + drflac_assert(bs != NULL); drflac_assert(pResult != NULL); drflac_assert(bitCount > 0); drflac_assert(bitCount <= 8); - drflac_int32 result; - if (!drflac__read_int32(bs, bitCount, &result)) { + if (!drflac__read_int32(bs, bitCount, &result)) return DRFLAC_FALSE; - } *pResult = (drflac_int8)result; return DRFLAC_TRUE; @@ -1487,98 +1497,98 @@ static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drf static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) { - if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += (drflac_uint32)bitsToSeek; - bs->cache <<= bitsToSeek; - return DRFLAC_TRUE; - } else { - // It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. - bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); - bs->cache = 0; + if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) + { + bs->consumedBits += (drflac_uint32)bitsToSeek; + bs->cache <<= bitsToSeek; + return DRFLAC_TRUE; + } - // Simple case. Seek in groups of the same number as bits that fit within a cache line. + /* It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. */ + bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->cache = 0; + + /* Simple case. Seek in groups of the same number as bits that fit within a cache line. */ #ifdef DRFLAC_64BIT - while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - drflac_uint64 bin; - if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return DRFLAC_FALSE; - } - bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); - } + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) + { + drflac_uint64 bin; + if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) + return DRFLAC_FALSE; + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } #else - while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - drflac_uint32 bin; - if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return DRFLAC_FALSE; - } - bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); - } + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) + { + drflac_uint32 bin; + if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) + return DRFLAC_FALSE; + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } #endif - // Whole leftover bytes. - while (bitsToSeek >= 8) { - drflac_uint8 bin; - if (!drflac__read_uint8(bs, 8, &bin)) { - return DRFLAC_FALSE; - } - bitsToSeek -= 8; - } + /* Whole leftover bytes. */ + while (bitsToSeek >= 8) + { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, 8, &bin)) + return DRFLAC_FALSE; + bitsToSeek -= 8; + } - // Leftover bits. - if (bitsToSeek > 0) { - drflac_uint8 bin; - if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { - return DRFLAC_FALSE; - } - bitsToSeek = 0; // <-- Necessary for the assert below. - } + /* Leftover bits. */ + if (bitsToSeek > 0) + { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) + return DRFLAC_FALSE; + bitsToSeek = 0; /* <-- Necessary for the assert below. */ + } - drflac_assert(bitsToSeek == 0); - return DRFLAC_TRUE; - } + drflac_assert(bitsToSeek == 0); + return DRFLAC_TRUE; } -// This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. +/* This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. */ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) { drflac_assert(bs != NULL); - // The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first - // thing to do is align to the next byte. - if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + /* The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first + * thing to do is align to the next byte. */ + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) return DRFLAC_FALSE; - } - for (;;) { + for (;;) + { #ifndef DR_FLAC_NO_CRC drflac__reset_crc16(bs); #endif drflac_uint8 hi; - if (!drflac__read_uint8(bs, 8, &hi)) { + if (!drflac__read_uint8(bs, 8, &hi)) return DRFLAC_FALSE; - } - if (hi == 0xFF) { - drflac_uint8 lo; - if (!drflac__read_uint8(bs, 6, &lo)) { - return DRFLAC_FALSE; - } + if (hi == 0xFF) + { + drflac_uint8 lo; + if (!drflac__read_uint8(bs, 6, &lo)) + return DRFLAC_FALSE; - if (lo == 0x3E) { - return DRFLAC_TRUE; - } else { - if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return DRFLAC_FALSE; - } - } + if (lo == 0x3E) + return DRFLAC_TRUE; + + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) + return DRFLAC_FALSE; } } - // Should never get here. - //return DRFLAC_FALSE; +#if 0 + /* Should never get here. */ + return DRFLAC_FALSE; +#endif } @@ -1598,9 +1608,10 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 }; - drflac_uint32 n = clz_table_4[x >> (sizeof(x)*8 - 4)]; - if (n == 0) { + + if (n == 0) + { #ifdef DRFLAC_64BIT if ((x & 0xFFFFFFFF00000000ULL) == 0) { n = 32; x <<= 32; } if ((x & 0xFFFF000000000000ULL) == 0) { n += 16; x <<= 16; } @@ -1620,7 +1631,7 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported() { - // If the compiler itself does not support the intrinsic then we'll need to return false. + /* If the compiler itself does not support the intrinsic then we'll need to return false. */ #ifdef DRFLAC_HAS_LZCNT_INTRINSIC return drflac__gIsLZCNTSupported; #else @@ -1631,22 +1642,22 @@ static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported() static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { #if defined(_MSC_VER) && !defined(__clang__) - #ifdef DRFLAC_64BIT - return (drflac_uint32)__lzcnt64(x); - #else - return (drflac_uint32)__lzcnt(x); - #endif +#ifdef DRFLAC_64BIT + return (drflac_uint32)__lzcnt64(x); #else - #if defined(__GNUC__) || defined(__clang__) - #ifdef DRFLAC_64BIT - return (drflac_uint32)__builtin_clzll((unsigned long long)x); - #else - return (drflac_uint32)__builtin_clzl((unsigned long)x); - #endif - #else - // Unsupported compiler. - #error "This compiler does not support the lzcnt intrinsic." - #endif + return (drflac_uint32)__lzcnt(x); +#endif +#else +#if defined(__GNUC__) || defined(__clang__) +#ifdef DRFLAC_64BIT + return (drflac_uint32)__builtin_clzll((unsigned long long)x); +#else + return (drflac_uint32)__builtin_clzl((unsigned long)x); +#endif +#else + /* Unsupported compiler. */ +#error "This compiler does not support the lzcnt intrinsic." +#endif #endif } #endif @@ -1654,42 +1665,39 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) { - drflac_uint32 n; + drflac_uint32 n; #ifdef DRFLAC_64BIT - _BitScanReverse64((unsigned long*)&n, x); + _BitScanReverse64((unsigned long*)&n, x); #else - _BitScanReverse((unsigned long*)&n, x); + _BitScanReverse((unsigned long*)&n, x); #endif - return sizeof(x)*8 - n - 1; + return sizeof(x)*8 - n - 1; } #endif static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { - // This function assumes at least one bit is set. Checking for 0 needs to be done at a higher level, outside this function. + /* This function assumes at least one bit is set. Checking for 0 needs to be done at a higher level, outside this function. */ #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT - if (drflac__is_lzcnt_supported()) { - return drflac__clz_lzcnt(x); - } else + if (drflac__is_lzcnt_supported()) + return drflac__clz_lzcnt(x); +#endif + +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC + return drflac__clz_msvc(x); +#else + return drflac__clz_software(x); #endif - { - #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC - return drflac__clz_msvc(x); - #else - return drflac__clz_software(x); - #endif - } } - -static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +static INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) { drflac_uint32 zeroCounter = 0; - while (bs->cache == 0) { - zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } + while (bs->cache == 0) + { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) + return DRFLAC_FALSE; } drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); @@ -1710,35 +1718,36 @@ static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFro drflac_assert(bs != NULL); drflac_assert(offsetFromStart > 0); - // Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which - // is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit. - // To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder. - if (offsetFromStart > 0x7FFFFFFF) { + /* Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which + * is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit. + * To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder. */ + if (offsetFromStart > 0x7FFFFFFF) + { drflac_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) return DRFLAC_FALSE; - } bytesRemaining -= 0x7FFFFFFF; - while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + while (bytesRemaining > 0x7FFFFFFF) + { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) return DRFLAC_FALSE; - } bytesRemaining -= 0x7FFFFFFF; } - if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { + if (bytesRemaining > 0) + { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) return DRFLAC_FALSE; - } - } - } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { - return DRFLAC_FALSE; } } + else + { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) + return DRFLAC_FALSE; + } - // The cache should be reset to force a reload of fresh data from the client. + /* The cache should be reset to force a reload of fresh data from the client. */ drflac__reset_cache(bs); return DRFLAC_TRUE; } @@ -1746,125 +1755,136 @@ static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFro static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) { - drflac_assert(bs != NULL); - drflac_assert(pNumberOut != NULL); + int i; + int byteCount = 1; + unsigned char utf8[7] = {0}; - drflac_uint8 crc = *pCRCOut; + drflac_assert(bs != NULL); + drflac_assert(pNumberOut != NULL); - unsigned char utf8[7] = {0}; - if (!drflac__read_uint8(bs, 8, utf8)) { - *pNumberOut = 0; - return DRFLAC_END_OF_STREAM; - } - crc = drflac_crc8(crc, utf8[0], 8); + drflac_uint8 crc = *pCRCOut; - if ((utf8[0] & 0x80) == 0) { - *pNumberOut = utf8[0]; - *pCRCOut = crc; - return DRFLAC_SUCCESS; - } + if (!drflac__read_uint8(bs, 8, utf8)) + { + *pNumberOut = 0; + return DRFLAC_END_OF_STREAM; + } + crc = drflac_crc8(crc, utf8[0], 8); - int byteCount = 1; - if ((utf8[0] & 0xE0) == 0xC0) { - byteCount = 2; - } else if ((utf8[0] & 0xF0) == 0xE0) { - byteCount = 3; - } else if ((utf8[0] & 0xF8) == 0xF0) { - byteCount = 4; - } else if ((utf8[0] & 0xFC) == 0xF8) { - byteCount = 5; - } else if ((utf8[0] & 0xFE) == 0xFC) { - byteCount = 6; - } else if ((utf8[0] & 0xFF) == 0xFE) { - byteCount = 7; - } else { - *pNumberOut = 0; - return DRFLAC_CRC_MISMATCH; // Bad UTF-8 encoding. - } + if ((utf8[0] & 0x80) == 0) + { + *pNumberOut = utf8[0]; + *pCRCOut = crc; + return DRFLAC_SUCCESS; + } - // Read extra bytes. - drflac_assert(byteCount > 1); + if ((utf8[0] & 0xE0) == 0xC0) + byteCount = 2; + else if ((utf8[0] & 0xF0) == 0xE0) + byteCount = 3; + else if ((utf8[0] & 0xF8) == 0xF0) + byteCount = 4; + else if ((utf8[0] & 0xFC) == 0xF8) + byteCount = 5; + else if ((utf8[0] & 0xFE) == 0xFC) + byteCount = 6; + else if ((utf8[0] & 0xFF) == 0xFE) + byteCount = 7; + else + { + *pNumberOut = 0; + return DRFLAC_CRC_MISMATCH; /* Bad UTF-8 encoding. */ + } - drflac_uint64 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); - for (int i = 1; i < byteCount; ++i) { - if (!drflac__read_uint8(bs, 8, utf8 + i)) { - *pNumberOut = 0; - return DRFLAC_END_OF_STREAM; - } - crc = drflac_crc8(crc, utf8[i], 8); + /* Read extra bytes. */ + drflac_assert(byteCount > 1); - result = (result << 6) | (utf8[i] & 0x3F); - } + drflac_uint64 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + for (i = 1; i < byteCount; ++i) + { + if (!drflac__read_uint8(bs, 8, utf8 + i)) + { + *pNumberOut = 0; + return DRFLAC_END_OF_STREAM; + } + crc = drflac_crc8(crc, utf8[i], 8); - *pNumberOut = result; - *pCRCOut = crc; - return DRFLAC_SUCCESS; + result = (result << 6) | (utf8[i] & 0x3F); + } + + *pNumberOut = result; + *pCRCOut = crc; + return DRFLAC_SUCCESS; } -// The next two functions are responsible for calculating the prediction. -// -// When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's -// safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. +/* The next two functions are responsible for calculating the prediction. + * + * When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's + * safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. + */ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { - drflac_assert(order <= 32); + drflac_int32 prediction = 0; - // 32-bit version. + drflac_assert(order <= 32); - // VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. - drflac_int32 prediction = 0; + /* 32-bit version. - switch (order) - { - case 32: prediction += coefficients[31] * pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; - } + * VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. */ - return (drflac_int32)(prediction >> shift); + switch (order) + { + case 32: prediction += coefficients[31] * pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; + } + + return (drflac_int32)(prediction >> shift); } static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { + drflac_int64 prediction; drflac_assert(order <= 32); - // 64-bit version. + /* 64-bit version. + + * This method is faster on the 32-bit build when compiling with VC++. See note below. + */ - // This method is faster on the 32-bit build when compiling with VC++. See note below. #ifndef DRFLAC_64BIT - drflac_int64 prediction; if (order == 8) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; @@ -1981,17 +2001,17 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 } else { - prediction = 0; - for (int j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; - } + int j; + prediction = 0; + for (j = 0; j < (int)order; ++j) + prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; } #endif - // VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some - // reason. The ugly version above is faster so we'll just switch between the two depending on the target platform. + /* VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some + * reason. The ugly version above is faster so we'll just switch between the two depending on the target platform. */ #ifdef DRFLAC_64BIT - drflac_int64 prediction = 0; + prediction = 0; switch (order) { @@ -2034,51 +2054,49 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 } #if 0 -// Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the -// sake of readability and should only be used as a reference. +/* Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the + * sake of readability and should only be used as a reference. */ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_assert(bs != NULL); drflac_assert(count > 0); drflac_assert(pSamplesOut != NULL); - for (drflac_uint32 i = 0; i < count; ++i) { + for (drflac_uint32 i = 0; i < count; ++i) + { drflac_uint32 zeroCounter = 0; - for (;;) { + for (;;) + { drflac_uint8 bit; - if (!drflac__read_uint8(bs, 1, &bit)) { + if (!drflac__read_uint8(bs, 1, &bit)) return DRFLAC_FALSE; - } - if (bit == 0) { + if (bit == 0) zeroCounter += 1; - } else { + else break; - } } drflac_uint32 decodedRice; - if (riceParam > 0) { - if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + if (riceParam > 0) + { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) return DRFLAC_FALSE; - } - } else { - decodedRice = 0; } + else + decodedRice = 0; decodedRice |= (zeroCounter << riceParam); - if ((decodedRice & 0x01)) { + if ((decodedRice & 0x01)) decodedRice = ~(decodedRice >> 1); - } else { + else decodedRice = (decodedRice >> 1); - } - if (bitsPerSample > 16) { + if (bitsPerSample > 16) pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); - } else { + else pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); - } } return DRFLAC_TRUE; @@ -2091,25 +2109,23 @@ static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_ui drflac_uint32 zeroCounter = 0; for (;;) { drflac_uint8 bit; - if (!drflac__read_uint8(bs, 1, &bit)) { + if (!drflac__read_uint8(bs, 1, &bit)) return DRFLAC_FALSE; - } - if (bit == 0) { + if (bit == 0) zeroCounter += 1; - } else { + else break; - } } drflac_uint32 decodedRice; - if (riceParam > 0) { - if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + if (riceParam > 0) + { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) return DRFLAC_FALSE; - } - } else { - decodedRice = 0; } + else + decodedRice = 0; *pZeroCounterOut = zeroCounter; *pRiceParamPartOut = decodedRice; @@ -2126,9 +2142,8 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac drflac_uint32 zeroCounter = 0; while (bs->cache == 0) { zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) { + if (!drflac__reload_cache(bs)) return DRFLAC_FALSE; - } } drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); @@ -2149,9 +2164,9 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac bs->cache <<= setBitOffsetPlus1; } - // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. + /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ drflac_uint32 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); - drflac_cache_t resultHi = bs->cache & riceParamMask; // <-- This mask is OK because all bits after the first bits are always zero. + drflac_cache_t resultHi = bs->cache & riceParamMask; /* <-- This mask is OK because all bits after the first bits are always zero. */ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC @@ -2163,7 +2178,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac bs->crc16Cache = bs->cache; #endif } else { - // Slow path. We need to fetch more data from the client. + /* Slow path. We need to fetch more data from the client. */ if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } @@ -2183,6 +2198,8 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { + drflac_uint32 i = 0; + drflac_assert(bs != NULL); drflac_assert(count > 0); drflac_assert(pSamplesOut != NULL); @@ -2190,24 +2207,29 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_b drflac_uint32 zeroCountPart; drflac_uint32 riceParamPart; - drflac_uint32 i = 0; - while (i < count) { - // Rice extraction. - if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { - return DRFLAC_FALSE; - } - // Rice reconstruction. - static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + while (i < count) + { + static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + /* Rice extraction. */ + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) + return DRFLAC_FALSE; + + /* Rice reconstruction. */ riceParamPart |= (zeroCountPart << riceParam); riceParamPart = (riceParamPart >> 1) ^ t[riceParamPart & 0x01]; - //riceParamPart = (riceParamPart >> 1) ^ (~(riceParamPart & 0x01) + 1); +#if 0 + riceParamPart = (riceParamPart >> 1) ^ (~(riceParamPart & 0x01) + 1); +#endif - // Sample reconstruction. - if (bitsPerSample > 16) { + /* Sample reconstruction. */ + if (bitsPerSample > 16) + { pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); - } else { + } + else + { pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); } @@ -2226,18 +2248,19 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, d #endif } -// Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. +/* Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. */ static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) { + drflac_uint32 i; drflac_assert(bs != NULL); drflac_assert(count > 0); - for (drflac_uint32 i = 0; i < count; ++i) { - drflac_uint32 zeroCountPart; - drflac_uint32 riceParamPart; - if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { - return DRFLAC_FALSE; - } + for (i = 0; i < count; ++i) + { + drflac_uint32 zeroCountPart; + drflac_uint32 riceParamPart; + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) + return DRFLAC_FALSE; } return DRFLAC_TRUE; @@ -2245,221 +2268,214 @@ static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_ static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { - drflac_assert(bs != NULL); - drflac_assert(count > 0); - drflac_assert(unencodedBitsPerSample > 0 && unencodedBitsPerSample <= 32); - drflac_assert(pSamplesOut != NULL); + unsigned int i; + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(unencodedBitsPerSample > 0 && unencodedBitsPerSample <= 32); + drflac_assert(pSamplesOut != NULL); - for (unsigned int i = 0; i < count; ++i) { - if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return DRFLAC_FALSE; - } + for (i = 0; i < count; ++i) + { + if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) + return DRFLAC_FALSE; - if (bitsPerSample > 16) { - pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); - } - } + if (bitsPerSample > 16) + { + pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + } + else + { + pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + } + } - return DRFLAC_TRUE; + return DRFLAC_TRUE; } -// Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called -// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The -// and parameters are used to determine how many residual values need to be decoded. +/* Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called + * when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The + * and parameters are used to determine how many residual values need to be decoded. */ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { - drflac_assert(bs != NULL); - drflac_assert(blockSize != 0); - drflac_assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? + drflac_uint8 partitionOrder; + drflac_uint8 residualMethod; - drflac_uint8 residualMethod; - if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DRFLAC_FALSE; - } + drflac_assert(bs != NULL); + drflac_assert(blockSize != 0); + drflac_assert(pDecodedSamples != NULL); /* <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? */ - if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DRFLAC_FALSE; // Unknown or unsupported residual coding method. - } + if (!drflac__read_uint8(bs, 2, &residualMethod)) + return DRFLAC_FALSE; - // Ignore the first values. - pDecodedSamples += order; + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) + return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ + + /* Ignore the first values. */ + pDecodedSamples += order; - drflac_uint8 partitionOrder; - if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DRFLAC_FALSE; - } + if (!drflac__read_uint8(bs, 4, &partitionOrder)) + return DRFLAC_FALSE; - // From the FLAC spec: - // The Rice partition order in a Rice-coded residual section must be less than or equal to 8. - if (partitionOrder > 8) { - return DRFLAC_FALSE; - } + /* From the FLAC spec: + * The Rice partition order in a Rice-coded residual section must be less than or equal to 8. */ + if (partitionOrder > 8) + return DRFLAC_FALSE; - // Validation check. - if ((blockSize / (1 << partitionOrder)) <= order) { - return DRFLAC_FALSE; - } + /* Validation check. */ + if ((blockSize / (1 << partitionOrder)) <= order) + return DRFLAC_FALSE; - drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - drflac_uint32 partitionsRemaining = (1 << partitionOrder); - for (;;) { - drflac_uint8 riceParam = 0; - if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DRFLAC_FALSE; - } - if (riceParam == 16) { - riceParam = 0xFF; - } - } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DRFLAC_FALSE; - } - if (riceParam == 32) { - riceParam = 0xFF; - } - } + drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + drflac_uint32 partitionsRemaining = (1 << partitionOrder); + for (;;) + { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) + { + if (!drflac__read_uint8(bs, 4, &riceParam)) + return DRFLAC_FALSE; + if (riceParam == 16) + riceParam = 0xFF; + } + else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) + { + if (!drflac__read_uint8(bs, 5, &riceParam)) + return DRFLAC_FALSE; + if (riceParam == 32) + riceParam = 0xFF; + } - if (riceParam != 0xFF) { - if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; - } - } else { - unsigned char unencodedBitsPerSample = 0; - if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DRFLAC_FALSE; - } + if (riceParam != 0xFF) + { + if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) + return DRFLAC_FALSE; + } + else + { + unsigned char unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) + return DRFLAC_FALSE; - if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; - } - } + if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) + return DRFLAC_FALSE; + } - pDecodedSamples += samplesInPartition; + pDecodedSamples += samplesInPartition; - if (partitionsRemaining == 1) { - break; - } + if (partitionsRemaining == 1) + break; - partitionsRemaining -= 1; + partitionsRemaining -= 1; - if (partitionOrder != 0) { - samplesInPartition = blockSize / (1 << partitionOrder); - } - } + if (partitionOrder != 0) + samplesInPartition = blockSize / (1 << partitionOrder); + } - return DRFLAC_TRUE; + return DRFLAC_TRUE; } -// Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called -// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be set to 0. The -// and parameters are used to determine how many residual values need to be decoded. +/* Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called + * when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be set to 0. The + * and parameters are used to determine how many residual values need to be decoded. */ static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) { - drflac_assert(bs != NULL); - drflac_assert(blockSize != 0); + drflac_uint8 partitionOrder; + drflac_uint8 residualMethod; - drflac_uint8 residualMethod; - if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DRFLAC_FALSE; - } + drflac_assert(bs != NULL); + drflac_assert(blockSize != 0); - if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DRFLAC_FALSE; // Unknown or unsupported residual coding method. - } + if (!drflac__read_uint8(bs, 2, &residualMethod)) + return DRFLAC_FALSE; - drflac_uint8 partitionOrder; - if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DRFLAC_FALSE; - } + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) + return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ - drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - drflac_uint32 partitionsRemaining = (1 << partitionOrder); - for (;;) - { - drflac_uint8 riceParam = 0; - if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DRFLAC_FALSE; - } - if (riceParam == 16) { - riceParam = 0xFF; - } - } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DRFLAC_FALSE; - } - if (riceParam == 32) { - riceParam = 0xFF; - } - } + if (!drflac__read_uint8(bs, 4, &partitionOrder)) + return DRFLAC_FALSE; - if (riceParam != 0xFF) { - if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return DRFLAC_FALSE; - } - } else { - unsigned char unencodedBitsPerSample = 0; - if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DRFLAC_FALSE; - } + drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + drflac_uint32 partitionsRemaining = (1 << partitionOrder); + for (;;) + { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) + { + if (!drflac__read_uint8(bs, 4, &riceParam)) + return DRFLAC_FALSE; + if (riceParam == 16) + riceParam = 0xFF; + } + else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) + return DRFLAC_FALSE; + if (riceParam == 32) + riceParam = 0xFF; + } - if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return DRFLAC_FALSE; - } - } + if (riceParam != 0xFF) + { + if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) + return DRFLAC_FALSE; + } + else + { + unsigned char unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) + return DRFLAC_FALSE; + if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) + return DRFLAC_FALSE; + } - if (partitionsRemaining == 1) { - break; - } + if (partitionsRemaining == 1) + break; - partitionsRemaining -= 1; - samplesInPartition = blockSize / (1 << partitionOrder); - } + partitionsRemaining -= 1; + samplesInPartition = blockSize / (1 << partitionOrder); + } - return DRFLAC_TRUE; + return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples) { - // Only a single sample needs to be decoded here. + drflac_uint32 i; + /* Only a single sample needs to be decoded here. */ drflac_int32 sample; - if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + if (!drflac__read_int32(bs, bitsPerSample, &sample)) return DRFLAC_FALSE; - } - // We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely) - // we'll want to look at a more efficient way. - for (drflac_uint32 i = 0; i < blockSize; ++i) { + /* We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely) + * we'll want to look at a more efficient way. */ + for (i = 0; i < blockSize; ++i) pDecodedSamples[i] = sample; - } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples) { - for (drflac_uint32 i = 0; i < blockSize; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DRFLAC_FALSE; - } + drflac_uint32 i; + for (i = 0; i < blockSize; ++i) + { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) + return DRFLAC_FALSE; - pDecodedSamples[i] = sample; - } + pDecodedSamples[i] = sample; + } - return DRFLAC_TRUE; + return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { + drflac_uint32 i; drflac_int32 lpcCoefficientsTable[5][4] = { {0, 0, 0, 0}, {1, 0, 0, 0}, @@ -2468,20 +2484,19 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 {4, -6, 4, -1} }; - // Warm up samples and coefficients. - for (drflac_uint32 i = 0; i < lpcOrder; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DRFLAC_FALSE; - } + /* Warm up samples and coefficients. */ + for (i = 0; i < lpcOrder; ++i) + { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) + return DRFLAC_FALSE; - pDecodedSamples[i] = sample; + pDecodedSamples[i] = sample; } - if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) return DRFLAC_FALSE; - } return DRFLAC_TRUE; } @@ -2489,43 +2504,38 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { drflac_uint8 i; - - // Warm up samples. - for (i = 0; i < lpcOrder; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DRFLAC_FALSE; - } - - pDecodedSamples[i] = sample; - } - drflac_uint8 lpcPrecision; - if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + drflac_int8 lpcShift; + drflac_int32 coefficients[32]; + + /* Warm up samples. */ + for (i = 0; i < lpcOrder; ++i) + { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) + return DRFLAC_FALSE; + + pDecodedSamples[i] = sample; + } + + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) return DRFLAC_FALSE; - } - if (lpcPrecision == 15) { - return DRFLAC_FALSE; // Invalid. - } + if (lpcPrecision == 15) + return DRFLAC_FALSE; /* Invalid. */ lpcPrecision += 1; - drflac_int8 lpcShift; - if (!drflac__read_int8(bs, 5, &lpcShift)) { + if (!drflac__read_int8(bs, 5, &lpcShift)) return DRFLAC_FALSE; - } - - drflac_int32 coefficients[32]; - for (i = 0; i < lpcOrder; ++i) { - if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { + for (i = 0; i < lpcOrder; ++i) + { + if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) return DRFLAC_FALSE; - } } - if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) { + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) return DRFLAC_FALSE; - } return DRFLAC_TRUE; } @@ -2537,255 +2547,243 @@ static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8 drflac_assert(header != NULL); const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; // -1 = reserved. + const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; /* -1 = reserved. */ - // Keep looping until we find a valid sync code. - for (;;) { - if (!drflac__find_and_seek_to_next_sync_code(bs)) { - return DRFLAC_FALSE; - } + /* Keep looping until we find a valid sync code. */ + for (;;) + { + drflac_bool32 isVariableBlockSize = false; + drflac_uint8 blockSize = 0; + drflac_uint8 blockingStrategy = 0; + drflac_uint8 crc8 = 0xCE; /* 0xCE = drflac_crc8(0, 0x3FFE, 14); */ + drflac_uint8 reserved = 0; + drflac_uint8 sampleRate = 0; + drflac_uint8 channelAssignment = 0; + drflac_uint8 bitsPerSample = 0; - drflac_uint8 crc8 = 0xCE; // 0xCE = drflac_crc8(0, 0x3FFE, 14); + if (!drflac__find_and_seek_to_next_sync_code(bs)) + return DRFLAC_FALSE; - drflac_uint8 reserved = 0; - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, reserved, 1); + if (!drflac__read_uint8(bs, 1, &reserved)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, reserved, 1); + if (!drflac__read_uint8(bs, 1, &blockingStrategy)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, blockingStrategy, 1); - drflac_uint8 blockingStrategy = 0; - if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, blockingStrategy, 1); + if (!drflac__read_uint8(bs, 4, &blockSize)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, blockSize, 4); + if (!drflac__read_uint8(bs, 4, &sampleRate)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, sampleRate, 4); - drflac_uint8 blockSize = 0; - if (!drflac__read_uint8(bs, 4, &blockSize)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, blockSize, 4); + if (!drflac__read_uint8(bs, 4, &channelAssignment)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, channelAssignment, 4); + if (!drflac__read_uint8(bs, 3, &bitsPerSample)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, bitsPerSample, 3); - drflac_uint8 sampleRate = 0; - if (!drflac__read_uint8(bs, 4, &sampleRate)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, sampleRate, 4); + if (!drflac__read_uint8(bs, 1, &reserved)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, reserved, 1); + isVariableBlockSize = blockingStrategy == 1; - drflac_uint8 channelAssignment = 0; - if (!drflac__read_uint8(bs, 4, &channelAssignment)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, channelAssignment, 4); - - - drflac_uint8 bitsPerSample = 0; - if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, bitsPerSample, 3); - - - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, reserved, 1); - - - drflac_bool32 isVariableBlockSize = blockingStrategy == 1; - if (isVariableBlockSize) { - drflac_uint64 sampleNumber; - drflac_result result = drflac__read_utf8_coded_number(bs, &sampleNumber, &crc8); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_END_OF_STREAM) { - return DRFLAC_FALSE; - } else { - continue; - } - } - header->frameNumber = 0; - header->sampleNumber = sampleNumber; - } else { - drflac_uint64 frameNumber = 0; - drflac_result result = drflac__read_utf8_coded_number(bs, &frameNumber, &crc8); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_END_OF_STREAM) { - return DRFLAC_FALSE; - } else { - continue; - } - } - header->frameNumber = (drflac_uint32)frameNumber; // <-- Safe cast. - header->sampleNumber = 0; - } - - - if (blockSize == 1) { - header->blockSize = 192; - } else if (blockSize >= 2 && blockSize <= 5) { - header->blockSize = 576 * (1 << (blockSize - 2)); - } else if (blockSize == 6) { - if (!drflac__read_uint16(bs, 8, &header->blockSize)) { + if (isVariableBlockSize) + { + drflac_uint64 sampleNumber; + drflac_result result = drflac__read_utf8_coded_number(bs, &sampleNumber, &crc8); + if (result != DRFLAC_SUCCESS) + { + if (result == DRFLAC_END_OF_STREAM) return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->blockSize, 8); - header->blockSize += 1; - } else if (blockSize == 7) { - if (!drflac__read_uint16(bs, 16, &header->blockSize)) { + else + continue; + } + header->frameNumber = 0; + header->sampleNumber = sampleNumber; + } + else + { + drflac_uint64 frameNumber = 0; + drflac_result result = drflac__read_utf8_coded_number(bs, &frameNumber, &crc8); + if (result != DRFLAC_SUCCESS) + { + if (result == DRFLAC_END_OF_STREAM) return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->blockSize, 16); - header->blockSize += 1; - } else { - header->blockSize = 256 * (1 << (blockSize - 8)); - } + else + continue; + } + header->frameNumber = (drflac_uint32)frameNumber; /* <-- Safe cast. */ + header->sampleNumber = 0; + } - if (sampleRate <= 11) { - header->sampleRate = sampleRateTable[sampleRate]; - } else if (sampleRate == 12) { - if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->sampleRate, 8); - header->sampleRate *= 1000; - } else if (sampleRate == 13) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->sampleRate, 16); - } else if (sampleRate == 14) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->sampleRate, 16); - header->sampleRate *= 10; - } else { - continue; // Invalid. Assume an invalid block. - } + if (blockSize == 1) + header->blockSize = 192; + else if (blockSize >= 2 && blockSize <= 5) + header->blockSize = 576 * (1 << (blockSize - 2)); + else if (blockSize == 6) + { + if (!drflac__read_uint16(bs, 8, &header->blockSize)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, header->blockSize, 8); + header->blockSize += 1; + } + else if (blockSize == 7) + { + if (!drflac__read_uint16(bs, 16, &header->blockSize)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, header->blockSize, 16); + header->blockSize += 1; + } + else + header->blockSize = 256 * (1 << (blockSize - 8)); + if (sampleRate <= 11) + header->sampleRate = sampleRateTable[sampleRate]; + else if (sampleRate == 12) + { + if (!drflac__read_uint32(bs, 8, &header->sampleRate)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, header->sampleRate, 8); + header->sampleRate *= 1000; + } + else if (sampleRate == 13) + { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + } + else if (sampleRate == 14) + { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) + return DRFLAC_FALSE; + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + header->sampleRate *= 10; + } + else + continue; /* Invalid. Assume an invalid block. */ - header->channelAssignment = channelAssignment; + header->channelAssignment = channelAssignment; - header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; - if (header->bitsPerSample == 0) { - header->bitsPerSample = streaminfoBitsPerSample; - } + header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; + if (header->bitsPerSample == 0) + header->bitsPerSample = streaminfoBitsPerSample; - if (!drflac__read_uint8(bs, 8, &header->crc8)) { - return DRFLAC_FALSE; - } + if (!drflac__read_uint8(bs, 8, &header->crc8)) + return DRFLAC_FALSE; - #ifndef DR_FLAC_NO_CRC - if (header->crc8 != crc8) { - continue; // CRC mismatch. Loop back to the top and find the next sync code. - } - #endif - return DRFLAC_TRUE; +#ifndef DR_FLAC_NO_CRC + if (header->crc8 != crc8) + continue; /* CRC mismatch. Loop back to the top and find the next sync code. */ +#endif + return DRFLAC_TRUE; } } static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) { - drflac_uint8 header; - if (!drflac__read_uint8(bs, 8, &header)) { - return DRFLAC_FALSE; - } + int type; + drflac_uint8 header; + if (!drflac__read_uint8(bs, 8, &header)) + return DRFLAC_FALSE; - // First bit should always be 0. - if ((header & 0x80) != 0) { - return DRFLAC_FALSE; - } + /* First bit should always be 0. */ + if ((header & 0x80) != 0) + return DRFLAC_FALSE; - int type = (header & 0x7E) >> 1; - if (type == 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; - } else if (type == 1) { - pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; - } else { - if ((type & 0x20) != 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (type & 0x1F) + 1; - } else if ((type & 0x08) != 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (type & 0x07); - if (pSubframe->lpcOrder > 4) { - pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; - pSubframe->lpcOrder = 0; - } - } else { + type = (header & 0x7E) >> 1; + if (type == 0) + pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; + else if (type == 1) + pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; + else + { + if ((type & 0x20) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (type & 0x1F) + 1; + } else if ((type & 0x08) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (type & 0x07); + if (pSubframe->lpcOrder > 4) { pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; - } - } + pSubframe->lpcOrder = 0; + } + } else { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + } + } - if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { - return DRFLAC_FALSE; - } + if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) + return DRFLAC_FALSE; - // Wasted bits per sample. - pSubframe->wastedBitsPerSample = 0; - if ((header & 0x01) == 1) { - unsigned int wastedBitsPerSample; - if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return DRFLAC_FALSE; - } - pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1; - } + /* Wasted bits per sample. */ + pSubframe->wastedBitsPerSample = 0; + if ((header & 0x01) == 1) + { + unsigned int wastedBitsPerSample; + if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) + return DRFLAC_FALSE; + pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1; + } - return DRFLAC_TRUE; + return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) { - drflac_assert(bs != NULL); - drflac_assert(frame != NULL); + drflac_subframe* pSubframe = NULL; + drflac_assert(bs != NULL); + drflac_assert(frame != NULL); - drflac_subframe* pSubframe = frame->subframes + subframeIndex; - if (!drflac__read_subframe_header(bs, pSubframe)) { - return DRFLAC_FALSE; - } + pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) + return DRFLAC_FALSE; - // Side channels require an extra bit per sample. Took a while to figure that one out... - pSubframe->bitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - pSubframe->bitsPerSample += 1; - } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - pSubframe->bitsPerSample += 1; - } + /* Side channels require an extra bit per sample. Took a while to figure that one out... */ + pSubframe->bitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + pSubframe->bitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + pSubframe->bitsPerSample += 1; + } - // Need to handle wasted bits per sample. - pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pDecodedSamples = pDecodedSamplesOut; + /* Need to handle wasted bits per sample. */ + pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pDecodedSamples = pDecodedSamplesOut; - switch (pSubframe->subframeType) - { - case DRFLAC_SUBFRAME_CONSTANT: - { + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { drflac__decode_samples__constant(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples); - } break; + } break; - case DRFLAC_SUBFRAME_VERBATIM: - { + case DRFLAC_SUBFRAME_VERBATIM: + { drflac__decode_samples__verbatim(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples); - } break; + } break; - case DRFLAC_SUBFRAME_FIXED: - { + case DRFLAC_SUBFRAME_FIXED: + { drflac__decode_samples__fixed(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples); - } break; + } break; - case DRFLAC_SUBFRAME_LPC: - { + case DRFLAC_SUBFRAME_LPC: + { drflac__decode_samples__lpc(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples); - } break; + } break; - default: return DRFLAC_FALSE; - } + default: return DRFLAC_FALSE; + } - return DRFLAC_TRUE; + return DRFLAC_TRUE; } static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) @@ -2794,11 +2792,10 @@ static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, i drflac_assert(frame != NULL); drflac_subframe* pSubframe = frame->subframes + subframeIndex; - if (!drflac__read_subframe_header(bs, pSubframe)) { + if (!drflac__read_subframe_header(bs, pSubframe)) return DRFLAC_FALSE; - } - // Side channels require an extra bit per sample. Took a while to figure that one out... + /* Side channels require an extra bit per sample. Took a while to figure that one out... */ pSubframe->bitsPerSample = frame->header.bitsPerSample; if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { pSubframe->bitsPerSample += 1; @@ -2806,7 +2803,7 @@ static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, i pSubframe->bitsPerSample += 1; } - // Need to handle wasted bits per sample. + /* Need to handle wasted bits per sample. */ pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pDecodedSamples = NULL; @@ -2814,56 +2811,47 @@ static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, i { case DRFLAC_SUBFRAME_CONSTANT: { - if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) { + if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) return DRFLAC_FALSE; - } } break; case DRFLAC_SUBFRAME_VERBATIM: { unsigned int bitsToSeek = frame->header.blockSize * pSubframe->bitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { + if (!drflac__seek_bits(bs, bitsToSeek)) return DRFLAC_FALSE; - } } break; case DRFLAC_SUBFRAME_FIXED: { unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { + if (!drflac__seek_bits(bs, bitsToSeek)) return DRFLAC_FALSE; - } - if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { + if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) return DRFLAC_FALSE; - } } break; case DRFLAC_SUBFRAME_LPC: { - unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; - } - unsigned char lpcPrecision; - if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) return DRFLAC_FALSE; - } - if (lpcPrecision == 15) { - return DRFLAC_FALSE; // Invalid. - } + + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) + return DRFLAC_FALSE; + if (lpcPrecision == 15) + return DRFLAC_FALSE; /* Invalid. */ lpcPrecision += 1; - bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; // +5 for shift. - if (!drflac__seek_bits(bs, bitsToSeek)) { + bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; /* +5 for shift. */ + if (!drflac__seek_bits(bs, bitsToSeek)) return DRFLAC_FALSE; - } - if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { + if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) return DRFLAC_FALSE; - } } break; default: return DRFLAC_FALSE; @@ -2883,46 +2871,43 @@ static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignm static drflac_result drflac__decode_frame(drflac* pFlac) { - // This function should be called while the stream is sitting on the first byte after the frame header. + int i, channelCount; + /* This function should be called while the stream is sitting on the first byte after the frame header. */ drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes)); - // The frame block size must never be larger than the maximum block size defined by the FLAC stream. - if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) { + /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */ + if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) return DRFLAC_ERROR; - } - // The number of channels in the frame must match the channel count from the STREAMINFO block. - int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - if (channelCount != (int)pFlac->channels) { + /* The number of channels in the frame must match the channel count from the STREAMINFO block. */ + channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + if (channelCount != (int)pFlac->channels) return DRFLAC_ERROR; - } - for (int i = 0; i < channelCount; ++i) { - if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) { - return DRFLAC_ERROR; - } + for (i = 0; i < channelCount; ++i) + { + if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) + return DRFLAC_ERROR; } drflac_uint8 paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7; - if (paddingSizeInBits > 0) { - drflac_uint8 padding = 0; - if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return DRFLAC_END_OF_STREAM; - } + if (paddingSizeInBits > 0) + { + drflac_uint8 padding = 0; + if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) + return DRFLAC_END_OF_STREAM; } #ifndef DR_FLAC_NO_CRC drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif drflac_uint16 desiredCRC16; - if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) return DRFLAC_END_OF_STREAM; - } #ifndef DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return DRFLAC_CRC_MISMATCH; // CRC mismatch. - } + if (actualCRC16 != desiredCRC16) + return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ #endif pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount; @@ -2932,77 +2917,78 @@ static drflac_result drflac__decode_frame(drflac* pFlac) static drflac_result drflac__seek_frame(drflac* pFlac) { - int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - for (int i = 0; i < channelCount; ++i) { - if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) { - return DRFLAC_ERROR; - } - } + drflac_uint16 desiredCRC16; + int i; + int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - // Padding. - if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { - return DRFLAC_ERROR; - } + for (i = 0; i < channelCount; ++i) + { + if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) + return DRFLAC_ERROR; + } - // CRC. + /* Padding. */ + if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) + return DRFLAC_ERROR; + + /* CRC. */ #ifndef DR_FLAC_NO_CRC - drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); + drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif - drflac_uint16 desiredCRC16; - if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return DRFLAC_END_OF_STREAM; - } + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) + return DRFLAC_END_OF_STREAM; #ifndef DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return DRFLAC_CRC_MISMATCH; // CRC mismatch. - } + if (actualCRC16 != desiredCRC16) + return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ #endif - return DRFLAC_SUCCESS; + return DRFLAC_SUCCESS; } static drflac_bool32 drflac__read_and_decode_next_frame(drflac* pFlac) { - drflac_assert(pFlac != NULL); + drflac_assert(pFlac != NULL); - for (;;) { - if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { - return DRFLAC_FALSE; - } + for (;;) + { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) + return DRFLAC_FALSE; - drflac_result result = drflac__decode_frame(pFlac); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Skip to the next frame. - } else { - return DRFLAC_FALSE; - } - } + drflac_result result = drflac__decode_frame(pFlac); + if (result != DRFLAC_SUCCESS) + { + if (result == DRFLAC_CRC_MISMATCH) + continue; /* CRC mismatch. Skip to the next frame. */ + return DRFLAC_FALSE; + } - return DRFLAC_TRUE; - } + return DRFLAC_TRUE; + } } static void drflac__get_current_frame_sample_range(drflac* pFlac, drflac_uint64* pFirstSampleInFrameOut, drflac_uint64* pLastSampleInFrameOut) { - drflac_assert(pFlac != NULL); + unsigned int channelCount; + drflac_uint64 firstSampleInFrame, lastSampleInFrame; - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + drflac_assert(pFlac != NULL); - drflac_uint64 firstSampleInFrame = pFlac->currentFrame.header.sampleNumber; - if (firstSampleInFrame == 0) { - firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount; - } + channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - drflac_uint64 lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount); - if (lastSampleInFrame > 0) { - lastSampleInFrame -= 1; // Needs to be zero based. - } + firstSampleInFrame = pFlac->currentFrame.header.sampleNumber; + if (firstSampleInFrame == 0) + firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount; - if (pFirstSampleInFrameOut) *pFirstSampleInFrameOut = firstSampleInFrame; - if (pLastSampleInFrameOut) *pLastSampleInFrameOut = lastSampleInFrame; + lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount); + if (lastSampleInFrame > 0) + lastSampleInFrame -= 1; /* Needs to be zero based. */ + + if (pFirstSampleInFrameOut) + *pFirstSampleInFrameOut = firstSampleInFrame; + if (pLastSampleInFrameOut) + *pLastSampleInFrameOut = lastSampleInFrame; } static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) @@ -3017,62 +3003,65 @@ static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac) { - // This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. + /* This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. */ drflac_assert(pFlac != NULL); return drflac__seek_frame(pFlac); } static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex) { - // We need to find the frame that contains the sample. To do this, we iterate over each frame and inspect it's header. If based on the - // header we can determine that the frame contains the sample, we do a full decode of that frame. - if (!drflac__seek_to_first_frame(pFlac)) { - return DRFLAC_FALSE; - } - drflac_uint64 runningSampleCount = 0; - for (;;) { - if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { - return DRFLAC_FALSE; - } - drflac_uint64 firstSampleInFrame = 0; - drflac_uint64 lastSampleInFrame = 0; - drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + /* We need to find the frame that contains the sample. To do this, we iterate over each frame and inspect it's header. If based on the + * header we can determine that the frame contains the sample, we do a full decode of that frame. */ + if (!drflac__seek_to_first_frame(pFlac)) + return DRFLAC_FALSE; - drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; - if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { - // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend - // it never existed and keep iterating. - drflac_result result = drflac__decode_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. - drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. - if (samplesToDecode == 0) { - return DRFLAC_TRUE; - } - return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. - } else { - return DRFLAC_FALSE; - } - } - } else { - // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this - // frame never existed and leave the running sample count untouched. - drflac_result result = drflac__seek_to_next_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningSampleCount += sampleCountInThisFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. - } else { - return DRFLAC_FALSE; - } - } - } + for (;;) + { + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) + return DRFLAC_FALSE; + + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) + { + /* The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + * it never existed and keep iterating. */ + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) + { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */ + if (samplesToDecode == 0) + return DRFLAC_TRUE; + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; /* <-- If this fails, something bad has happened (it should never fail). */ + } + else + { + if (result == DRFLAC_CRC_MISMATCH) + continue; /* CRC mismatch. Pretend this frame never existed. */ + return DRFLAC_FALSE; + } + } + else + { + /* It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + * frame never existed and leave the running sample count untouched. */ + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) + runningSampleCount += sampleCountInThisFrame; + else + { + if (result == DRFLAC_CRC_MISMATCH) + continue; /* CRC mismatch. Pretend this frame never existed. */ + return DRFLAC_FALSE; + } + } } } @@ -3081,95 +3070,90 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui { drflac_assert(pFlac != NULL); - if (pFlac->seektablePos == 0) { + if (pFlac->seektablePos == 0) return DRFLAC_FALSE; - } - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->seektablePos)) { + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->seektablePos)) return DRFLAC_FALSE; - } - - // The number of seek points is derived from the size of the SEEKTABLE block. - drflac_uint32 seekpointCount = pFlac->seektableSize / 18; // 18 = the size of each seek point. - if (seekpointCount == 0) { - return DRFLAC_FALSE; // Would this ever happen? - } + /* The number of seek points is derived from the size of the SEEKTABLE block. */ + drflac_uint32 seekpointCount = pFlac->seektableSize / 18; /* 18 = the size of each seek point. */ + if (seekpointCount == 0) + return DRFLAC_FALSE; /* Would this ever happen? */ drflac_seekpoint closestSeekpoint = {0, 0, 0}; drflac_uint32 seekpointsRemaining = seekpointCount; while (seekpointsRemaining > 0) { drflac_seekpoint seekpoint; - if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) { + if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) break; - } - if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) { + if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) break; - } - if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) { + if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) break; - } - // Note that the seekpoint sample is based on a single channel. The input sample (sampleIndex) is based on interleaving, thus - // we need to multiple the seekpoint's sample by the channel count. - if (seekpoint.firstSample*pFlac->channels > sampleIndex) { + /* Note that the seekpoint sample is based on a single channel. The input sample (sampleIndex) is based on interleaving, thus + * we need to multiple the seekpoint's sample by the channel count. */ + if (seekpoint.firstSample*pFlac->channels > sampleIndex) break; - } closestSeekpoint = seekpoint; seekpointsRemaining -= 1; } - // At this point we should have found the seekpoint closest to our sample. We need to seek to it using basically the same - // technique as we use with the brute force method. - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + closestSeekpoint.frameOffset)) { + /* At this point we should have found the seekpoint closest to our sample. We need to seek to it using basically the same + * technique as we use with the brute force method. */ + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + closestSeekpoint.frameOffset)) return DRFLAC_FALSE; - } drflac_uint64 runningSampleCount = closestSeekpoint.firstSample*pFlac->channels; - for (;;) { - if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { - return DRFLAC_FALSE; - } + for (;;) + { + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; - drflac_uint64 firstSampleInFrame = 0; - drflac_uint64 lastSampleInFrame = 0; - drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) + return DRFLAC_FALSE; - drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; - if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { - // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend - // it never existed and keep iterating. - drflac_result result = drflac__decode_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. - drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. - if (samplesToDecode == 0) { - return DRFLAC_TRUE; - } - return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. - } else { - return DRFLAC_FALSE; - } - } - } else { - // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this - // frame never existed and leave the running sample count untouched. - drflac_result result = drflac__seek_to_next_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningSampleCount += sampleCountInThisFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. - } else { - return DRFLAC_FALSE; - } - } - } + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) + { + /* The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + * it never existed and keep iterating. */ + drflac_result result = drflac__decode_frame(pFlac); + + if (result == DRFLAC_SUCCESS) + { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */ + if (samplesToDecode == 0) + return DRFLAC_TRUE; + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; /* <-- If this fails, something bad has happened (it should never fail). */ + } + else + { + if (result == DRFLAC_CRC_MISMATCH) + continue; /* CRC mismatch. Pretend this frame never existed. */ + return DRFLAC_FALSE; + } + } + else + { + /* It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + * frame never existed and leave the running sample count untouched. */ + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) + runningSampleCount += sampleCountInThisFrame; + else + { + if (result == DRFLAC_CRC_MISMATCH) + continue; /* CRC mismatch. Pretend this frame never existed. */ + return DRFLAC_FALSE; + } + } } } @@ -3177,8 +3161,8 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui #ifndef DR_FLAC_NO_OGG typedef struct { - drflac_uint8 capturePattern[4]; // Should be "OggS" - drflac_uint8 structureVersion; // Always 0. + drflac_uint8 capturePattern[4]; /* Should be "OggS" */ + drflac_uint8 structureVersion; /* Always 0. */ drflac_uint8 headerType; drflac_uint64 granulePosition; drflac_uint32 serialNumber; @@ -3205,8 +3189,8 @@ typedef struct drflac_uint64 runningFilePos; drflac_bool32 hasStreamInfoBlock; drflac_bool32 hasMetadataBlocks; - drflac_bs bs; // <-- A bit streamer is required for loading data during initialization. - drflac_frame_header firstFrameHeader; // <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. + drflac_bs bs; /* <-- A bit streamer is required for loading data during initialization. */ + drflac_frame_header firstFrameHeader; /* <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. */ #ifndef DR_FLAC_NO_OGG drflac_uint32 oggSerial; @@ -3236,29 +3220,26 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_r drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) { - // min/max block size. drflac_uint32 blockSizes; - if (onRead(pUserData, &blockSizes, 4) != 4) { - return DRFLAC_FALSE; - } - - // min/max frame size. drflac_uint64 frameSizes = 0; - if (onRead(pUserData, &frameSizes, 6) != 6) { - return DRFLAC_FALSE; - } - - // Sample rate, channels, bits per sample and total sample count. drflac_uint64 importantProps; - if (onRead(pUserData, &importantProps, 8) != 8) { - return DRFLAC_FALSE; - } - - // MD5 drflac_uint8 md5[16]; - if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { + + /* min/max block size. */ + if (onRead(pUserData, &blockSizes, 4) != 4) + return DRFLAC_FALSE; + + /* min/max frame size. */ + if (onRead(pUserData, &frameSizes, 6) != 6) + return DRFLAC_FALSE; + + /* Sample rate, channels, bits per sample and total sample count. */ + if (onRead(pUserData, &importantProps, 8) != 8) + return DRFLAC_FALSE; + + /* MD5 */ + if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) return DRFLAC_FALSE; - } blockSizes = drflac__be2host_32(blockSizes); frameSizes = drflac__be2host_64(frameSizes); @@ -3281,236 +3262,236 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) { drflac_assert(pFlac != NULL); - // We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that - // we'll be sitting on byte 42. + /* We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that + * we'll be sitting on byte 42. */ drflac_uint64 runningFilePos = 42; drflac_uint64 seektablePos = 0; drflac_uint32 seektableSize = 0; - for (;;) { - drflac_uint8 isLastBlock = 0; - drflac_uint8 blockType; - drflac_uint32 blockSize; - if (!drflac__read_and_decode_block_header(pFlac->bs.onRead, pFlac->bs.pUserData, &isLastBlock, &blockType, &blockSize)) { - return DRFLAC_FALSE; - } - runningFilePos += 4; + for (;;) + { + drflac_uint8 isLastBlock = 0; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(pFlac->bs.onRead, pFlac->bs.pUserData, &isLastBlock, &blockType, &blockSize)) + return DRFLAC_FALSE; + runningFilePos += 4; - drflac_metadata metadata; - metadata.type = blockType; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; + drflac_metadata metadata; + metadata.type = blockType; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; - switch (blockType) - { - case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: - { + switch (blockType) + { + case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: + { if (pFlac->onMeta) { - void* pRawData = DRFLAC_MALLOC(blockSize); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) + return DRFLAC_FALSE; - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - DRFLAC_FREE(pRawData); - return DRFLAC_FALSE; - } + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); - metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); - metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); + metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); + pFlac->onMeta(pFlac->pUserDataMD, &metadata); - DRFLAC_FREE(pRawData); + DRFLAC_FREE(pRawData); } - } break; + } break; - case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: - { + case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + { seektablePos = runningFilePos; seektableSize = blockSize; - if (pFlac->onMeta) { - void* pRawData = DRFLAC_MALLOC(blockSize); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } + if (pFlac->onMeta) + { + drflac_uint32 iSeekpoint; + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) + return DRFLAC_FALSE; - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - DRFLAC_FREE(pRawData); - return DRFLAC_FALSE; - } + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint); - metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint); + metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; - // Endian swap. - for (drflac_uint32 iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { - drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; - pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample); - pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset); - pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount); - } + /* Endian swap. */ + for (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { + drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample); + pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset); + pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount); + } - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + pFlac->onMeta(pFlac->pUserDataMD, &metadata); - DRFLAC_FREE(pRawData); + DRFLAC_FREE(pRawData); } - } break; + } break; - case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: - { - if (pFlac->onMeta) { - void* pRawData = DRFLAC_MALLOC(blockSize); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } + case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + { + if (pFlac->onMeta) + { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) + return DRFLAC_FALSE; - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - DRFLAC_FREE(pRawData); - return DRFLAC_FALSE; - } + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; - const char* pRunningData = (const char*)pRawData; - metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.vorbis_comment.comments = pRunningData; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + const char* pRunningData = (const char*)pRawData; + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.comments = pRunningData; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); - DRFLAC_FREE(pRawData); + DRFLAC_FREE(pRawData); } - } break; + } break; - case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: - { - if (pFlac->onMeta) { - void* pRawData = DRFLAC_MALLOC(blockSize); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } + case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: + { + if (pFlac->onMeta) + { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) + return DRFLAC_FALSE; - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - DRFLAC_FREE(pRawData); - return DRFLAC_FALSE; - } + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; - const char* pRunningData = (const char*)pRawData; - drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(drflac_uint64*)pRunningData); pRunningData += 4; - metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; - metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + const char* pRunningData = (const char*)pRawData; + drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(drflac_uint64*)pRunningData); pRunningData += 4; + metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; + metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; + metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); - DRFLAC_FREE(pRawData); + DRFLAC_FREE(pRawData); } - } break; + } break; - case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: - { - if (pFlac->onMeta) { - void* pRawData = DRFLAC_MALLOC(blockSize); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } + case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: + { + if (pFlac->onMeta) + { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) + return DRFLAC_FALSE; - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - DRFLAC_FREE(pRawData); - return DRFLAC_FALSE; - } + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; - const char* pRunningData = (const char*)pRawData; - metadata.data.picture.type = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.description = pRunningData; - metadata.data.picture.width = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.height = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + const char* pRunningData = (const char*)pRawData; + metadata.data.picture.type = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.description = pRunningData; + metadata.data.picture.width = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); - DRFLAC_FREE(pRawData); + DRFLAC_FREE(pRawData); } - } break; + } break; - case DRFLAC_METADATA_BLOCK_TYPE_PADDING: - { - if (pFlac->onMeta) { - metadata.data.padding.unused = 0; + case DRFLAC_METADATA_BLOCK_TYPE_PADDING: + { + if (pFlac->onMeta) + { + metadata.data.padding.unused = 0; - // Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. - } else { - pFlac->onMeta(pFlac->pUserDataMD, &metadata); - } + /* Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. */ + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) + isLastBlock = DRFLAC_TRUE; /* An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ + else + pFlac->onMeta(pFlac->pUserDataMD, &metadata); } - } break; + } + break; - case DRFLAC_METADATA_BLOCK_TYPE_INVALID: - { - // Invalid chunk. Just skip over this one. - if (pFlac->onMeta) { - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. - } + case DRFLAC_METADATA_BLOCK_TYPE_INVALID: + { + /* Invalid chunk. Just skip over this one. */ + if (pFlac->onMeta) + { + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) + isLastBlock = DRFLAC_TRUE; /* An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ } - } + } - default: - { - // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we - // can at the very least report the chunk to the application and let it look at the raw data. - if (pFlac->onMeta) { - void* pRawData = DRFLAC_MALLOC(blockSize); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } + default: + { + /* It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we + * can at the very least report the chunk to the application and let it look at the raw data. */ + if (pFlac->onMeta) + { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) + return DRFLAC_FALSE; - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - DRFLAC_FREE(pRawData); - return DRFLAC_FALSE; - } + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); - DRFLAC_FREE(pRawData); + DRFLAC_FREE(pRawData); } - } break; - } + } break; + } - // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. - if (pFlac->onMeta == NULL && blockSize > 0) { - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; - } - } + /* If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. */ + if (pFlac->onMeta == NULL && blockSize > 0) + { + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) + isLastBlock = DRFLAC_TRUE; + } - runningFilePos += blockSize; - if (isLastBlock) { - break; - } + runningFilePos += blockSize; + if (isLastBlock) + break; } pFlac->seektablePos = seektablePos; @@ -3522,56 +3503,55 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { - (void)onSeek; - - // Pre: The bit stream should be sitting just past the 4-byte id header. - - pInit->container = drflac_container_native; - - // The first metadata block should be the STREAMINFO block. drflac_uint8 isLastBlock; drflac_uint8 blockType; drflac_uint32 blockSize; - if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + + (void)onSeek; + + /* Pre: The bit stream should be sitting just past the 4-byte id header. */ + + pInit->container = drflac_container_native; + + /* The first metadata block should be the STREAMINFO block. */ + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) return DRFLAC_FALSE; + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) + { + /* We're opening in strict mode and the first block is not the STREAMINFO block. Error. */ + if (!relaxed) + return DRFLAC_FALSE; + + /* Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined + * for that frame. */ + pInit->hasStreamInfoBlock = DRFLAC_FALSE; + pInit->hasMetadataBlocks = DRFLAC_FALSE; + + if (!drflac__read_next_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) + return DRFLAC_FALSE; /* Couldn't find a frame. */ + + if (pInit->firstFrameHeader.bitsPerSample == 0) + return DRFLAC_FALSE; /* Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. */ + + pInit->sampleRate = pInit->firstFrameHeader.sampleRate; + pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; + pInit->maxBlockSize = 65535; /* <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo */ + return DRFLAC_TRUE; } - - if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - if (!relaxed) { - // We're opening in strict mode and the first block is not the STREAMINFO block. Error. - return DRFLAC_FALSE; - } else { - // Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined - // for that frame. - pInit->hasStreamInfoBlock = DRFLAC_FALSE; - pInit->hasMetadataBlocks = DRFLAC_FALSE; - - if (!drflac__read_next_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { - return DRFLAC_FALSE; // Couldn't find a frame. - } - - if (pInit->firstFrameHeader.bitsPerSample == 0) { - return DRFLAC_FALSE; // Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. - } - - pInit->sampleRate = pInit->firstFrameHeader.sampleRate; - pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); - pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; - pInit->maxBlockSize = 65535; // <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo - return DRFLAC_TRUE; - } - } else { + else + { drflac_streaminfo streaminfo; - if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) return DRFLAC_FALSE; - } pInit->hasStreamInfoBlock = DRFLAC_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; pInit->totalSampleCount = streaminfo.totalSampleCount; - pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation). + pInit->maxBlockSize = streaminfo.maxBlockSize; /* Don't care about the min block size - only the max (used for determining the size of the memory allocation). */ pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { @@ -3589,7 +3569,7 @@ drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_ #ifndef DR_FLAC_NO_OGG #define DRFLAC_OGG_MAX_PAGE_SIZE 65307 -#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 // CRC-32 of "OggS". +#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 /* CRC-32 of "OggS". */ typedef enum { @@ -3695,11 +3675,12 @@ static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drfl static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) { - // This can be optimized. - for (drflac_uint32 i = 0; i < dataSize; ++i) { - crc32 = drflac_crc32_byte(crc32, pData[i]); - } - return crc32; + drflac_uint32 i; + + /* This can be optimized. */ + for (i = 0; i < dataSize; ++i) + crc32 = drflac_crc32_byte(crc32, pData[i]); + return crc32; } @@ -3715,22 +3696,24 @@ static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_p static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) { - drflac_uint32 pageBodySize = 0; - for (int i = 0; i < pHeader->segmentCount; ++i) { - pageBodySize += pHeader->segmentTable[i]; - } + int i; + drflac_uint32 pageBodySize = 0; - return pageBodySize; + for (i = 0; i < pHeader->segmentCount; ++i) + pageBodySize += pHeader->segmentTable[i]; + + return pageBodySize; } drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { + drflac_uint32 i; + drflac_uint8 data[23]; + drflac_assert(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); - drflac_uint8 data[23]; - if (onRead(pUserData, data, 23) != 23) { + if (onRead(pUserData, data, 23) != 23) return DRFLAC_END_OF_STREAM; - } *pBytesRead += 23; pHeader->structureVersion = data[0]; @@ -3741,88 +3724,84 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro drflac_copy_memory(&pHeader->checksum, &data[18], 4); pHeader->segmentCount = data[22]; - // Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. + /* Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. */ data[18] = 0; data[19] = 0; data[20] = 0; data[21] = 0; - drflac_uint32 i; - for (i = 0; i < 23; ++i) { + for (i = 0; i < 23; ++i) *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); - } - - if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { + if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) return DRFLAC_END_OF_STREAM; - } + *pBytesRead += pHeader->segmentCount; - for (i = 0; i < pHeader->segmentCount; ++i) { + for (i = 0; i < pHeader->segmentCount; ++i) *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); - } return DRFLAC_SUCCESS; } drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { + drflac_uint8 id[4]; + *pBytesRead = 0; - drflac_uint8 id[4]; - if (onRead(pUserData, id, 4) != 4) { + if (onRead(pUserData, id, 4) != 4) return DRFLAC_END_OF_STREAM; - } *pBytesRead += 4; - // We need to read byte-by-byte until we find the OggS capture pattern. - for (;;) { - if (drflac_ogg__is_capture_pattern(id)) { - *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + /* We need to read byte-by-byte until we find the OggS capture pattern. */ + for (;;) + { + if (drflac_ogg__is_capture_pattern(id)) + { + *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; - drflac_result result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); - if (result == DRFLAC_SUCCESS) { - return DRFLAC_SUCCESS; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; - } else { - return result; - } - } - } else { - // The first 4 bytes did not equal the capture pattern. Read the next byte and try again. - id[0] = id[1]; - id[1] = id[2]; - id[2] = id[3]; - if (onRead(pUserData, &id[3], 1) != 1) { - return DRFLAC_END_OF_STREAM; - } - *pBytesRead += 1; + drflac_result result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == DRFLAC_SUCCESS) + return DRFLAC_SUCCESS; + + if (result == DRFLAC_CRC_MISMATCH) + continue; + return result; + } + else + { + /* The first 4 bytes did not equal the capture pattern. Read the next byte and try again. */ + id[0] = id[1]; + id[1] = id[2]; + id[2] = id[3]; + if (onRead(pUserData, &id[3], 1) != 1) + return DRFLAC_END_OF_STREAM; + *pBytesRead += 1; } } } -// The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works -// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is architecured -// in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type -// dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from -// the physical Ogg bitstream are converted and delivered in native FLAC format. +/* The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works + * in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is architecured + * in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type + * dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from + * the physical Ogg bitstream are converted and delivered in native FLAC format. */ typedef struct { - drflac_read_proc onRead; // The original onRead callback from drflac_open() and family. - drflac_seek_proc onSeek; // The original onSeek callback from drflac_open() and family. - void* pUserData; // The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. - drflac_uint64 currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. - drflac_uint64 firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. - drflac_uint32 serialNumber; // The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. - drflac_ogg_page_header bosPageHeader; // Used for seeking. + drflac_read_proc onRead; /* The original onRead callback from drflac_open() and family. */ + drflac_seek_proc onSeek; /* The original onSeek callback from drflac_open() and family. */ + void* pUserData; /* The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. */ + drflac_uint64 currentBytePos; /* The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. */ + drflac_uint64 firstBytePos; /* The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. */ + drflac_uint32 serialNumber; /* The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. */ + drflac_ogg_page_header bosPageHeader; /* Used for seeking. */ drflac_ogg_page_header currentPageHeader; drflac_uint32 bytesRemainingInPage; drflac_uint32 pageDataSize; drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; -} drflac_oggbs; // oggbs = Ogg Bitstream +} drflac_oggbs; /* oggbs = Ogg Bitstream */ static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) { @@ -3834,34 +3813,37 @@ static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) { - if (origin == drflac_seek_origin_start) { - if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { - return DRFLAC_FALSE; - } - oggbs->currentBytePos = offset; + if (origin == drflac_seek_origin_start) + { + if (offset <= 0x7FFFFFFF) + { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) + return DRFLAC_FALSE; + oggbs->currentBytePos = offset; - return DRFLAC_TRUE; - } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { - return DRFLAC_FALSE; - } - oggbs->currentBytePos = offset; + return DRFLAC_TRUE; + } + else + { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) + return DRFLAC_FALSE; + oggbs->currentBytePos = offset; - return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); - } - } else { - while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - oggbs->currentBytePos += 0x7FFFFFFF; - offset -= 0x7FFFFFFF; + return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); + } + } + else + { + while (offset > 0x7FFFFFFF) + { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) + return DRFLAC_FALSE; + oggbs->currentBytePos += 0x7FFFFFFF; + offset -= 0x7FFFFFFF; } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { // <-- Safe cast thanks to the loop above. + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) /* <-- Safe cast thanks to the loop above. */ return DRFLAC_FALSE; - } oggbs->currentBytePos += offset; return DRFLAC_TRUE; @@ -3874,43 +3856,39 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og for (;;) { drflac_uint32 crc32 = 0; drflac_uint32 bytesRead; - if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) return DRFLAC_FALSE; - } oggbs->currentBytePos += bytesRead; drflac_uint32 pageBodySize = drflac_ogg__get_page_body_size(&header); - if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { - continue; // Invalid page size. Assume it's corrupted and just move to the next page. - } + if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) + continue; /* Invalid page size. Assume it's corrupted and just move to the next page. */ - if (header.serialNumber != oggbs->serialNumber) { - // It's not a FLAC page. Skip it. - if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { + if (header.serialNumber != oggbs->serialNumber) + { + /* It's not a FLAC page. Skip it. */ + if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) return DRFLAC_FALSE; - } continue; } - - // We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. - if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + /* We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. */ + if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) return DRFLAC_FALSE; - } oggbs->pageDataSize = pageBodySize; #ifndef DR_FLAC_NO_CRC drflac_uint32 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); - if (actualCRC32 != header.checksum) { - if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { - continue; // CRC mismatch. Skip this page. - } else { - // Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we - // go to the next valid page to ensure we're in a good state, but return false to let the caller know that the - // seek did not fully complete. - drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); - return DRFLAC_FALSE; - } + if (actualCRC32 != header.checksum) + { + if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) + continue; /* CRC mismatch. Skip this page. */ + + /* Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we + * go to the next valid page to ensure we're in a good state, but return false to let the caller know that the + * seek did not fully complete. */ + drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); + return DRFLAC_FALSE; } #endif @@ -3920,21 +3898,21 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og } } -// Function below is unused at the moment, but I might be re-adding it later. +/* Function below is unused at the moment, but I might be re-adding it later. */ #if 0 static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) { drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; drflac_uint8 iSeg = 0; drflac_uint32 iByte = 0; - while (iByte < bytesConsumedInPage) { - drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (iByte + segmentSize > bytesConsumedInPage) { - break; - } else { - iSeg += 1; - iByte += segmentSize; - } + while (iByte < bytesConsumedInPage) + { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (iByte + segmentSize > bytesConsumedInPage) + break; + + iSeg += 1; + iByte += segmentSize; } *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); @@ -3943,7 +3921,7 @@ static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) { - // The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. + /* The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. */ for (;;) { drflac_bool32 atEndOfPage = DRFLAC_FALSE; @@ -3953,10 +3931,10 @@ static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (segmentSize < 255) { - if (iSeg == oggbs->currentPageHeader.segmentCount-1) { + if (segmentSize < 255) + { + if (iSeg == oggbs->currentPageHeader.segmentCount-1) atEndOfPage = DRFLAC_TRUE; - } break; } @@ -3964,24 +3942,25 @@ static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) bytesToEndOfPacketOrPage += segmentSize; } - // At this point we will have found either the packet or the end of the page. If were at the end of the page we'll - // want to load the next page and keep searching for the end of the packet. + /* At this point we will have found either the packet or the end of the page. If were at the end of the page we'll + * want to load the next page and keep searching for the end of the packet. */ drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; - if (atEndOfPage) { - // We're potentially at the next packet, but we need to check the next page first to be sure because the packet may - // straddle pages. - if (!drflac_oggbs__goto_next_page(oggbs)) { + if (atEndOfPage) + { + /* We're potentially at the next packet, but we need to check the next page first to be sure because the packet may + * straddle pages. */ + if (!drflac_oggbs__goto_next_page(oggbs)) return DRFLAC_FALSE; - } - // If it's a fresh packet it most likely means we're at the next packet. - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + /* If it's a fresh packet it most likely means we're at the next packet. */ + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) return DRFLAC_TRUE; - } - } else { - // We're at the next packet. + } + else + { + /* We're at the next packet. */ return DRFLAC_TRUE; } } @@ -3989,44 +3968,47 @@ static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) { - // The bitstream should be sitting on the first byte just after the header of the frame. + /* The bitstream should be sitting on the first byte just after the header of the frame. - // What we're actually doing here is seeking to the start of the next packet. + * What we're actually doing here is seeking to the start of the next packet. */ return drflac_oggbs__seek_to_next_packet(oggbs); } #endif static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) { + size_t bytesRead = 0; + drflac_uint8* pRunningBufferOut = NULL; drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + drflac_assert(oggbs != NULL); - drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; + pRunningBufferOut = (drflac_uint8*)bufferOut; - // Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. - size_t bytesRead = 0; - while (bytesRead < bytesToRead) { - size_t bytesRemainingToRead = bytesToRead - bytesRead; + /* Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. */ + while (bytesRead < bytesToRead) + { + size_t bytesRemainingToRead = bytesToRead - bytesRead; - if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); - bytesRead += bytesRemainingToRead; - oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; - break; - } + if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { + drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + bytesRead += bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; + break; + } - // If we get here it means some of the requested data is contained in the next pages. - if (oggbs->bytesRemainingInPage > 0) { - drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); - bytesRead += oggbs->bytesRemainingInPage; - pRunningBufferOut += oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } + /* If we get here it means some of the requested data is contained in the next pages. */ + if (oggbs->bytesRemainingInPage > 0) + { + drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + bytesRead += oggbs->bytesRemainingInPage; + pRunningBufferOut += oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } - drflac_assert(bytesRemainingToRead > 0); - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { - break; // Failed to go to the next page. Might have simply hit the end of the stream. - } + drflac_assert(bytesRemainingToRead > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) + break; /* Failed to go to the next page. Might have simply hit the end of the stream. */ } return bytesRead; @@ -4034,48 +4016,50 @@ static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytes static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) { + int bytesSeeked = 0; drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + drflac_assert(oggbs != NULL); drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); - // Seeking is always forward which makes things a lot simpler. - if (origin == drflac_seek_origin_start) { - if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { - return DRFLAC_FALSE; - } + /* Seeking is always forward which makes things a lot simpler. */ + if (origin == drflac_seek_origin_start) + { + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) + return DRFLAC_FALSE; - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { - return DRFLAC_FALSE; - } + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) + return DRFLAC_FALSE; - return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); + return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); } - drflac_assert(origin == drflac_seek_origin_current); - int bytesSeeked = 0; - while (bytesSeeked < offset) { - int bytesRemainingToSeek = offset - bytesSeeked; - drflac_assert(bytesRemainingToSeek >= 0); + while (bytesSeeked < offset) + { + int bytesRemainingToSeek = offset - bytesSeeked; + drflac_assert(bytesRemainingToSeek >= 0); - if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - bytesSeeked += bytesRemainingToSeek; - oggbs->bytesRemainingInPage -= bytesRemainingToSeek; - break; - } + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) + { + bytesSeeked += bytesRemainingToSeek; + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } - // If we get here it means some of the requested data is contained in the next pages. - if (oggbs->bytesRemainingInPage > 0) { - bytesSeeked += (int)oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } + /* If we get here it means some of the requested data is contained in the next pages. */ + if (oggbs->bytesRemainingInPage > 0) + { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } - drflac_assert(bytesRemainingToSeek > 0); - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { - // Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. - return DRFLAC_FALSE; - } + drflac_assert(bytesRemainingToSeek > 0); + + /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */ + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) + return DRFLAC_FALSE; } return DRFLAC_TRUE; @@ -4083,41 +4067,39 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) { + drflac_uint64 runningGranulePosition = 0; drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - drflac_uint64 originalBytePos = oggbs->currentBytePos; // For recovery. + drflac_uint64 originalBytePos = oggbs->currentBytePos; /* For recovery. */ - // First seek to the first frame. - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) { + /* First seek to the first frame. */ + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) return DRFLAC_FALSE; - } oggbs->bytesRemainingInPage = 0; - drflac_uint64 runningGranulePosition = 0; - drflac_uint64 runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier. + drflac_uint64 runningFrameBytePos = oggbs->currentBytePos; /* <-- Points to the OggS identifier. */ for (;;) { if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); - return DRFLAC_FALSE; // Never did find that sample... + return DRFLAC_FALSE; /* Never did find that sample... */ } runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; - if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) { - break; // The sample is somewhere in the previous page. - } + if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) + break; /* The sample is somewhere in the previous page. */ - - // At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we - // disregard any pages that do not begin a fresh packet. - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { // <-- Is it a fresh page? - if (oggbs->currentPageHeader.segmentTable[0] >= 2) { + /* At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we + * disregard any pages that do not begin a fresh packet. */ + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) + { /* <-- Is it a fresh page? */ + if (oggbs->currentPageHeader.segmentTable[0] >= 2) + { drflac_uint8 firstBytesInPage[2]; firstBytesInPage[0] = oggbs->pageData[0]; firstBytesInPage[1] = oggbs->pageData[1]; - if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { // <-- Does the page begin with a frame's sync code? + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) /* <-- Does the page begin with a frame's sync code? */ runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels; - } continue; } @@ -4125,243 +4107,242 @@ drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleInde } - // We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the - // start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of - // a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until - // we find the one containing the target sample. - if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { + /* We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the + * start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of + * a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until + * we find the one containing the target sample. */ + if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) return DRFLAC_FALSE; - } - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) return DRFLAC_FALSE; - } - - // At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep - // looping over these frames until we find the one containing the sample we're after. + /* At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep + * looping over these frames until we find the one containing the sample we're after. */ drflac_uint64 runningSampleCount = runningGranulePosition; - for (;;) { - // There are two ways to find the sample and seek past irrelevant frames: - // 1) Use the native FLAC decoder. - // 2) Use Ogg's framing system. - // - // Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to - // do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code - // duplication for the decoding of frame headers. - // - // Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg - // bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the - // standard drflac__*() APIs because that will read in extra data for it's own internal caching which in turn breaks - // the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read - // using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to - // avoid the use of the drflac_bs object. - // - // Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: - // 1) Seeking is already partially accellerated using Ogg's paging system in the code block above. - // 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. - // 3) Simplicity. - if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { - return DRFLAC_FALSE; - } + for (;;) + { + /* There are two ways to find the sample and seek past irrelevant frames: + * 1) Use the native FLAC decoder. + * 2) Use Ogg's framing system. + * + * Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to + * do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code + * duplication for the decoding of frame headers. + * + * Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg + * bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the + * standard drflac__*() APIs because that will read in extra data for it's own internal caching which in turn breaks + * the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read + * using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to + * avoid the use of the drflac_bs object. + * + * Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: + * 1) Seeking is already partially accellerated using Ogg's paging system in the code block above. + * 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. + * 3) Simplicity. + */ + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) + return DRFLAC_FALSE; - drflac_uint64 firstSampleInFrame = 0; - drflac_uint64 lastSampleInFrame = 0; - drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); - drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; - if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { - // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend - // it never existed and keep iterating. - drflac_result result = drflac__decode_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. - drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. - if (samplesToDecode == 0) { - return DRFLAC_TRUE; - } - return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. - } else { - return DRFLAC_FALSE; - } - } - } else { - // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this - // frame never existed and leave the running sample count untouched. - drflac_result result = drflac__seek_to_next_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningSampleCount += sampleCountInThisFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. - } else { - return DRFLAC_FALSE; - } - } - } + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + /* The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + * it never existed and keep iterating. */ + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */ + if (samplesToDecode == 0) + return DRFLAC_TRUE; + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; /* <-- If this fails, something bad has happened (it should never fail). */ + } + else + { + if (result == DRFLAC_CRC_MISMATCH) + continue; /* CRC mismatch. Pretend this frame never existed. */ + return DRFLAC_FALSE; + } + } + else + { + /* It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + * frame never existed and leave the running sample count untouched. */ + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) + runningSampleCount += sampleCountInThisFrame; + else + { + if (result == DRFLAC_CRC_MISMATCH) + continue; /* CRC mismatch. Pretend this frame never existed. */ + return DRFLAC_FALSE; + } + } } } drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { - // Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern. + /* We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the + * stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if + * any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. */ + drflac_ogg_page_header header; + + drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + drflac_uint32 bytesRead = 0; + /* Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern. */ (void)relaxed; pInit->container = drflac_container_ogg; pInit->oggFirstBytePos = 0; - // We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the - // stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if - // any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. - drflac_ogg_page_header header; - - drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; - drflac_uint32 bytesRead = 0; - if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) return DRFLAC_FALSE; - } pInit->runningFilePos += bytesRead; - for (;;) { - // Break if we're past the beginning of stream page. - if ((header.headerType & 0x02) == 0) { + for (;;) + { + int pageBodySize; + /* Break if we're past the beginning of stream page. */ + if ((header.headerType & 0x02) == 0) return DRFLAC_FALSE; + + /* Check if it's a FLAC header. */ + pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize == 51) + { + /* 51 = the lacing value of the FLAC header packet. + * It could be a FLAC page... */ + drflac_uint32 bytesRemainingInPage = pageBodySize; + + drflac_uint8 packetType; + if (onRead(pUserData, &packetType, 1) != 1) + return DRFLAC_FALSE; + + bytesRemainingInPage -= 1; + if (packetType == 0x7F) + { + /* Increasingly more likely to be a FLAC page... */ + drflac_uint8 sig[4]; + if (onRead(pUserData, sig, 4) != 4) + return DRFLAC_FALSE; + + bytesRemainingInPage -= 4; + if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { + /* Almost certainly a FLAC page... */ + drflac_uint8 mappingVersion[2]; + if (onRead(pUserData, mappingVersion, 2) != 2) + return DRFLAC_FALSE; + + if (mappingVersion[0] != 1) + return DRFLAC_FALSE; /* Only supporting version 1.x of the Ogg mapping. */ + + /* The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to + * be handling it in a generic way based on the serial number and packet types. */ + if (!onSeek(pUserData, 2, drflac_seek_origin_current)) + return DRFLAC_FALSE; + + /* Expecting the native FLAC signature "fLaC". */ + if (onRead(pUserData, sig, 4) != 4) + return DRFLAC_FALSE; + + if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') + { + /* The remaining data in the page should be the STREAMINFO block. */ + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) + return DRFLAC_FALSE; + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) + return DRFLAC_FALSE; /* Invalid block type. First block must be the STREAMINFO block. */ + + drflac_streaminfo streaminfo; + if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + /* Success! */ + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalSampleCount = streaminfo.totalSampleCount; + pInit->maxBlockSize = streaminfo.maxBlockSize; + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + + pInit->runningFilePos += pageBodySize; + pInit->oggFirstBytePos = pInit->runningFilePos - 79; /* Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page. */ + pInit->oggSerial = header.serialNumber; + pInit->oggBosHeader = header; + break; + } + else + { + /* Failed to read STREAMINFO block. Aww, so close... */ + return DRFLAC_FALSE; + } + } + else + { + /* Invalid file. */ + return DRFLAC_FALSE; + } + } + else + { + /* Not a FLAC header. Skip it. */ + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) + return DRFLAC_FALSE; + } + } + else + { + /* Not a FLAC header. Seek past the entire page and move on to the next. */ + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) + return DRFLAC_FALSE; + } } - - - // Check if it's a FLAC header. - int pageBodySize = drflac_ogg__get_page_body_size(&header); - if (pageBodySize == 51) { // 51 = the lacing value of the FLAC header packet. - // It could be a FLAC page... - drflac_uint32 bytesRemainingInPage = pageBodySize; - - drflac_uint8 packetType; - if (onRead(pUserData, &packetType, 1) != 1) { - return DRFLAC_FALSE; - } - - bytesRemainingInPage -= 1; - if (packetType == 0x7F) { - // Increasingly more likely to be a FLAC page... - drflac_uint8 sig[4]; - if (onRead(pUserData, sig, 4) != 4) { - return DRFLAC_FALSE; - } - - bytesRemainingInPage -= 4; - if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { - // Almost certainly a FLAC page... - drflac_uint8 mappingVersion[2]; - if (onRead(pUserData, mappingVersion, 2) != 2) { - return DRFLAC_FALSE; - } - - if (mappingVersion[0] != 1) { - return DRFLAC_FALSE; // Only supporting version 1.x of the Ogg mapping. - } - - // The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to - // be handling it in a generic way based on the serial number and packet types. - if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - - // Expecting the native FLAC signature "fLaC". - if (onRead(pUserData, sig, 4) != 4) { - return DRFLAC_FALSE; - } - - if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { - // The remaining data in the page should be the STREAMINFO block. - drflac_uint8 isLastBlock; - drflac_uint8 blockType; - drflac_uint32 blockSize; - if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return DRFLAC_FALSE; - } - - if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return DRFLAC_FALSE; // Invalid block type. First block must be the STREAMINFO block. - } - - drflac_streaminfo streaminfo; - if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { - // Success! - pInit->hasStreamInfoBlock = DRFLAC_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalSampleCount = streaminfo.totalSampleCount; - pInit->maxBlockSize = streaminfo.maxBlockSize; - pInit->hasMetadataBlocks = !isLastBlock; - - if (onMeta) { - drflac_metadata metadata; - metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - - pInit->runningFilePos += pageBodySize; - pInit->oggFirstBytePos = pInit->runningFilePos - 79; // Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page. - pInit->oggSerial = header.serialNumber; - pInit->oggBosHeader = header; - break; - } else { - // Failed to read STREAMINFO block. Aww, so close... - return DRFLAC_FALSE; - } - } else { - // Invalid file. - return DRFLAC_FALSE; - } - } else { - // Not a FLAC header. Skip it. - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - } - } else { - // Not a FLAC header. Seek past the entire page and move on to the next. - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - } - } else { - if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } + else + { + if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) + return DRFLAC_FALSE; } pInit->runningFilePos += pageBodySize; - - // Read the header of the next page. - if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + /* Read the header of the next page. */ + if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) return DRFLAC_FALSE; - } pInit->runningFilePos += bytesRead; } - // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next - // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialiation phase for Ogg is to create the - // Ogg bistream object. - pInit->hasMetadataBlocks = DRFLAC_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block. + /* If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next + * packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialiation phase for Ogg is to create the + * Ogg bistream object. */ + pInit->hasMetadataBlocks = DRFLAC_TRUE; /* <-- Always have at least VORBIS_COMMENT metadata block. */ return DRFLAC_TRUE; } #endif drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { - if (pInit == NULL || onRead == NULL || onSeek == NULL) { + drflac_uint8 id[4]; + + if (pInit == NULL || onRead == NULL || onSeek == NULL) return DRFLAC_FALSE; - } drflac_zero_memory(pInit, sizeof(*pInit)); pInit->onRead = onRead; @@ -4377,40 +4358,38 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR drflac__reset_cache(&pInit->bs); - // If the container is explicitly defined then we can try opening in relaxed mode. + /* If the container is explicitly defined then we can try opening in relaxed mode. */ drflac_bool32 relaxed = container != drflac_container_unknown; - drflac_uint8 id[4]; + /* Skip over any ID3 tags. */ + for (;;) + { + if (onRead(pUserData, id, 4) != 4) + return DRFLAC_FALSE; /* Ran out of data. */ + pInit->runningFilePos += 4; - // Skip over any ID3 tags. - for (;;) { - if (onRead(pUserData, id, 4) != 4) { - return DRFLAC_FALSE; // Ran out of data. - } - pInit->runningFilePos += 4; + if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') + { + drflac_uint8 flags; + drflac_uint32 headerSize; + drflac_uint8 header[6]; - if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { - drflac_uint8 header[6]; - if (onRead(pUserData, header, 6) != 6) { - return DRFLAC_FALSE; // Ran out of data. - } - pInit->runningFilePos += 6; + if (onRead(pUserData, header, 6) != 6) + return DRFLAC_FALSE; /* Ran out of data. */ + pInit->runningFilePos += 6; - drflac_uint8 flags = header[1]; - drflac_uint32 headerSize; - drflac_copy_memory(&headerSize, header+2, 4); - headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); - if (flags & 0x10) { - headerSize += 10; - } + flags = header[1]; + drflac_copy_memory(&headerSize, header+2, 4); + headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); + if (flags & 0x10) + headerSize += 10; - if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; // Failed to seek past the tag. - } - pInit->runningFilePos += headerSize; - } else { - break; - } + if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) + return DRFLAC_FALSE; /* Failed to seek past the tag. */ + pInit->runningFilePos += headerSize; + } + else + break; } if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { @@ -4422,19 +4401,18 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR } #endif - // If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. - if (relaxed) { - if (container == drflac_container_native) { - return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } + /* If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. */ + if (relaxed) + { + if (container == drflac_container_native) + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); #ifndef DR_FLAC_NO_OGG - if (container == drflac_container_ogg) { - return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } + if (container == drflac_container_ogg) + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); #endif } - // Unsupported container. + /* Unsupported container. */ return DRFLAC_FALSE; } @@ -4457,27 +4435,28 @@ void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit) drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { + drflac_init_info init; + #ifndef DRFLAC_NO_CPUID - // CPU support first. + /* CPU support first. */ drflac__init_cpu_caps(); #endif - drflac_init_info init; - if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) return NULL; - } - // The size of the allocation for the drflac object needs to be large enough to fit the following: - // 1) The main members of the drflac structure - // 2) A block of memory large enough to store the decoded samples of the largest frame in the stream - // 3) If the container is Ogg, a drflac_oggbs object - // - // The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration - // the different SIMD instruction sets. + /* The size of the allocation for the drflac object needs to be large enough to fit the following: + * 1) The main members of the drflac structure + * 2) A block of memory large enough to store the decoded samples of the largest frame in the stream + * 3) If the container is Ogg, a drflac_oggbs object + * + * The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration + * the different SIMD instruction sets. + */ drflac_uint32 allocationSize = sizeof(drflac); - // The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector - // we are supporting. + /* The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector + * we are supporting. */ drflac_uint32 wholeSIMDVectorCountPerChannel; if ((init.maxBlockSize % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); @@ -4488,13 +4467,12 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p drflac_uint32 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; allocationSize += decodedSamplesAllocationSize; - allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; // Allocate extra bytes to ensure we have enough for alignment. + allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; /* Allocate extra bytes to ensure we have enough for alignment. */ #ifndef DR_FLAC_NO_OGG - // There's additional data required for Ogg streams. - if (init.container == drflac_container_ogg) { + /* There's additional data required for Ogg streams. */ + if (init.container == drflac_container_ogg) allocationSize += sizeof(drflac_oggbs); - } #endif drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize); @@ -4513,7 +4491,7 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p oggbs->bosPageHeader = init.oggBosHeader; oggbs->bytesRemainingInPage = 0; - // The Ogg bistream needs to be layered on top of the original bitstream. + /* The Ogg bistream needs to be layered on top of the original bitstream. */ pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; pFlac->bs.pUserData = (void*)oggbs; @@ -4521,43 +4499,40 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p } #endif - // Decode metadata before returning. - if (init.hasMetadataBlocks) { - if (!drflac__read_and_decode_metadata(pFlac)) { + /* Decode metadata before returning. */ + if (init.hasMetadataBlocks) + { + if (!drflac__read_and_decode_metadata(pFlac)) + { DRFLAC_FREE(pFlac); return NULL; } } - // If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode - // the first frame. - if (!init.hasStreamInfoBlock) { + /* If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode + * the first frame. */ + if (!init.hasStreamInfoBlock) + { pFlac->currentFrame.header = init.firstFrameHeader; do { - drflac_result result = drflac__decode_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - break; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { - DRFLAC_FREE(pFlac); - return NULL; - } - continue; - } else { - DRFLAC_FREE(pFlac); - return NULL; - } - } + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) + break; + + if (result == DRFLAC_CRC_MISMATCH) + { + if (drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) + continue; + } + DRFLAC_FREE(pFlac); + return NULL; } while (1); } return pFlac; } - - #ifndef DR_FLAC_NO_STDIO typedef void* drflac_file; @@ -4600,19 +4575,20 @@ static void drflac__close_file_handle(drflac_file file) #else #include -// This doesn't seem to be defined for VC6. +/* This doesn't seem to be defined for VC6. */ #ifndef INVALID_SET_FILE_POINTER #define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { - drflac_assert(bytesToRead < 0xFFFFFFFF); // dr_flac will never request huge amounts of data at a time. This is a safe assertion. + DWORD bytesRead; - DWORD bytesRead; - ReadFile((HANDLE)pUserData, bufferOut, (DWORD)bytesToRead, &bytesRead, NULL); + drflac_assert(bytesToRead < 0xFFFFFFFF); /* dr_flac will never request huge amounts of data at a time. This is a safe assertion. */ - return (size_t)bytesRead; + ReadFile((HANDLE)pUserData, bufferOut, (DWORD)bytesToRead, &bytesRead, NULL); + + return (size_t)bytesRead; } static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) @@ -4670,7 +4646,7 @@ drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc on return pFlac; } -#endif //DR_FLAC_NO_STDIO +#endif /* DR_FLAC_NO_STDIO */ static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { @@ -4697,17 +4673,17 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_ drflac_assert(memoryStream != NULL); drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); - if (origin == drflac_seek_origin_current) { - if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { + if (origin == drflac_seek_origin_current) + { + if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) memoryStream->currentReadPos += offset; - } else { - memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. - } + else + memoryStream->currentReadPos = memoryStream->dataSize; /* Trying to seek too far forward. */ } else { if ((drflac_uint32)offset <= memoryStream->dataSize) { memoryStream->currentReadPos = offset; } else { - memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. + memoryStream->currentReadPos = memoryStream->dataSize; /* Trying to seek too far forward. */ } } @@ -4721,13 +4697,12 @@ drflac* drflac_open_memory(const void* data, size_t dataSize) memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; drflac* pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream); - if (pFlac == NULL) { + if (pFlac == NULL) return NULL; - } pFlac->memoryStream = memoryStream; - // This is an awful hack... + /* This is an awful hack... */ #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { @@ -4750,13 +4725,12 @@ drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drfl memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData); - if (pFlac == NULL) { + if (pFlac == NULL) return NULL; - } pFlac->memoryStream = memoryStream; - // This is an awful hack... + /* This is an awful hack... */ #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { @@ -4799,20 +4773,19 @@ void drflac_close(drflac* pFlac) } #ifndef DR_FLAC_NO_STDIO - // If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file() - // was used by looking at the callbacks. - if (pFlac->bs.onRead == drflac__on_read_stdio) { + /* If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file() + * was used by looking at the callbacks. */ + if (pFlac->bs.onRead == drflac__on_read_stdio) drflac__close_file_handle((drflac_file)pFlac->bs.pUserData); - } #ifndef DR_FLAC_NO_OGG - // Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. - if (pFlac->container == drflac_container_ogg) { - drflac_assert(pFlac->bs.onRead == drflac__on_read_ogg); - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - if (oggbs->onRead == drflac__on_read_stdio) { - drflac__close_file_handle((drflac_file)oggbs->pUserData); - } + /* Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. */ + if (pFlac->container == drflac_container_ogg) + { + drflac_assert(pFlac->bs.onRead == drflac__on_read_ogg); + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + if (oggbs->onRead == drflac__on_read_stdio) + drflac__close_file_handle((drflac_file)oggbs->pUserData); } #endif #endif @@ -4822,82 +4795,82 @@ void drflac_close(drflac* pFlac) drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) { + drflac_uint64 samplesRead = 0; unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - // We should never be calling this when the number of samples to read is >= the sample count. + /* We should never be calling this when the number of samples to read is >= the sample count. */ drflac_assert(samplesToRead < channelCount); drflac_assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining); + while (samplesToRead > 0) + { + drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + drflac_uint64 channelIndex = samplesReadFromFrameSoFar % channelCount; - drflac_uint64 samplesRead = 0; - while (samplesToRead > 0) { - drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; - drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; - drflac_uint64 channelIndex = samplesReadFromFrameSoFar % channelCount; + drflac_uint64 nextSampleInFrame = samplesReadFromFrameSoFar / channelCount; - drflac_uint64 nextSampleInFrame = samplesReadFromFrameSoFar / channelCount; - - int decodedSample = 0; - switch (pFlac->currentFrame.header.channelAssignment) - { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { + int decodedSample = 0; + switch (pFlac->currentFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { if (channelIndex == 0) { - decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; } else { - int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; - int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; - decodedSample = left - side; + int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; + decodedSample = left - side; } - } break; + } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { if (channelIndex == 0) { - int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; - int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; - decodedSample = side + right; + int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; + decodedSample = side + right; } else { - decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; } - } break; + } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { int mid; int side; if (channelIndex == 0) { - mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; - side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; + mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; - mid = (((unsigned int)mid) << 1) | (side & 0x01); - decodedSample = (mid + side) >> 1; + mid = (((unsigned int)mid) << 1) | (side & 0x01); + decodedSample = (mid + side) >> 1; } else { - mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; - side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; + side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; - mid = (((unsigned int)mid) << 1) | (side & 0x01); - decodedSample = (mid - side) >> 1; + mid = (((unsigned int)mid) << 1) | (side & 0x01); + decodedSample = (mid - side) >> 1; } - } break; + } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; - } break; - } + } break; + } - decodedSample <<= ((32 - pFlac->bitsPerSample) + pFlac->currentFrame.subframes[channelIndex].wastedBitsPerSample); + decodedSample <<= ((32 - pFlac->bitsPerSample) + pFlac->currentFrame.subframes[channelIndex].wastedBitsPerSample); - if (bufferOut) { - *bufferOut++ = decodedSample; - } + if (bufferOut) { + *bufferOut++ = decodedSample; + } - samplesRead += 1; - pFlac->currentFrame.samplesRemaining -= 1; - samplesToRead -= 1; + samplesRead += 1; + pFlac->currentFrame.samplesRemaining -= 1; + samplesToRead -= 1; } return samplesRead; @@ -4907,11 +4880,13 @@ drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 sampl { drflac_uint64 samplesRead = 0; while (samplesToRead > 0) { - if (pFlac->currentFrame.samplesRemaining == 0) { - if (!drflac__read_and_decode_next_frame(pFlac)) { - break; // Couldn't read the next frame, so just break from the loop and return. - } - } else { + if (pFlac->currentFrame.samplesRemaining == 0) + { + if (!drflac__read_and_decode_next_frame(pFlac)) + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + else + { samplesRead += 1; pFlac->currentFrame.samplesRemaining -= 1; samplesToRead -= 1; @@ -4923,144 +4898,151 @@ drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 sampl drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) { - // Note that is allowed to be null, in which case this will be treated as something like a seek. - if (pFlac == NULL || samplesToRead == 0) { - return 0; - } - - if (bufferOut == NULL) { - return drflac__seek_forward_by_samples(pFlac, samplesToRead); - } - - drflac_uint64 samplesRead = 0; - while (samplesToRead > 0) { - // If we've run out of samples in this frame, go to the next. - if (pFlac->currentFrame.samplesRemaining == 0) { - if (!drflac__read_and_decode_next_frame(pFlac)) { - break; // Couldn't read the next frame, so just break from the loop and return. - } - } else { - // Here is where we grab the samples and interleave them. + /* Note that is allowed to be null, in which case this will be treated as something like a seek. */ + if (pFlac == NULL || samplesToRead == 0) + return 0; - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; - drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + if (bufferOut == NULL) + return drflac__seek_forward_by_samples(pFlac, samplesToRead); - drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; - if (misalignedSampleCount > 0) { - drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); - samplesRead += misalignedSamplesRead; - samplesReadFromFrameSoFar += misalignedSamplesRead; - bufferOut += misalignedSamplesRead; - samplesToRead -= misalignedSamplesRead; - } + while (samplesToRead > 0) + { + /* If we've run out of samples in this frame, go to the next. */ + if (pFlac->currentFrame.samplesRemaining == 0) + { + if (!drflac__read_and_decode_next_frame(pFlac)) + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + else + { + /* Here is where we grab the samples and interleave them. */ + + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + + drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; + if (misalignedSampleCount > 0) { + drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); + samplesRead += misalignedSamplesRead; + samplesReadFromFrameSoFar += misalignedSamplesRead; + bufferOut += misalignedSamplesRead; + samplesToRead -= misalignedSamplesRead; + } - drflac_uint64 alignedSampleCountPerChannel = samplesToRead / channelCount; - if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) { - alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount; - } + drflac_uint64 alignedSampleCountPerChannel = samplesToRead / channelCount; + if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) { + alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount; + } - drflac_uint64 firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount; - unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample; + drflac_uint64 firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount; + unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample; - switch (pFlac->currentFrame.header.channelAssignment) - { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + switch (pFlac->currentFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + drflac_uint64 i; + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; - for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { - int left = pDecodedSamples0[i]; - int side = pDecodedSamples1[i]; - int right = left - side; + for (i = 0; i < alignedSampleCountPerChannel; ++i) { + int left = pDecodedSamples0[i]; + int side = pDecodedSamples1[i]; + int right = left - side; - bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); - bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); - } + bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + drflac_uint64 i; + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; - for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { - int side = pDecodedSamples0[i]; - int right = pDecodedSamples1[i]; - int left = right + side; + for (i = 0; i < alignedSampleCountPerChannel; ++i) { + int side = pDecodedSamples0[i]; + int right = pDecodedSamples1[i]; + int left = right + side; - bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); - bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); - } + bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + drflac_uint64 i; + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; - for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { - int side = pDecodedSamples1[i]; - int mid = (((drflac_uint32)pDecodedSamples0[i]) << 1) | (side & 0x01); + for (i = 0; i < alignedSampleCountPerChannel; ++i) { + int side = pDecodedSamples1[i]; + int mid = (((drflac_uint32)pDecodedSamples0[i]) << 1) | (side & 0x01); - bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); - bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); - } + bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: { - if (pFlac->currentFrame.header.channelAssignment == 1) // 1 = Stereo - { - // Stereo optimized inner loop unroll. - const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + if (pFlac->currentFrame.header.channelAssignment == 1) /* 1 = Stereo */ + { + drflac_uint64 i; + /* Stereo optimized inner loop unroll. */ + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; - for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { - bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); - bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); - } - } - else - { - // Generic interleaving. - for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { - for (unsigned int j = 0; j < channelCount; ++j) { - bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample); - } - } - } + for (i = 0; i < alignedSampleCountPerChannel; ++i) { + bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } + else + { + drflac_uint64 i; + + /* Generic interleaving. */ + for (i = 0; i < alignedSampleCountPerChannel; ++i) + { + unsigned j; + for (j = 0; j < channelCount; ++j) + { + bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample); + } + } + } } break; - } + } - drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount; - samplesRead += alignedSamplesRead; - samplesReadFromFrameSoFar += alignedSamplesRead; - bufferOut += alignedSamplesRead; - samplesToRead -= alignedSamplesRead; - pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead; + drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount; + samplesRead += alignedSamplesRead; + samplesReadFromFrameSoFar += alignedSamplesRead; + bufferOut += alignedSamplesRead; + samplesToRead -= alignedSamplesRead; + pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead; + /* At this point we may still have some excess samples left to read. */ + if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) { + drflac_uint64 excessSamplesRead = 0; + if (samplesToRead < pFlac->currentFrame.samplesRemaining) { + excessSamplesRead = drflac__read_s32__misaligned(pFlac, samplesToRead, bufferOut); + } else { + excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut); + } - - // At this point we may still have some excess samples left to read. - if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) { - drflac_uint64 excessSamplesRead = 0; - if (samplesToRead < pFlac->currentFrame.samplesRemaining) { - excessSamplesRead = drflac__read_s32__misaligned(pFlac, samplesToRead, bufferOut); - } else { - excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut); - } - - samplesRead += excessSamplesRead; - samplesReadFromFrameSoFar += excessSamplesRead; - bufferOut += excessSamplesRead; - samplesToRead -= excessSamplesRead; - } - } + samplesRead += excessSamplesRead; + samplesReadFromFrameSoFar += excessSamplesRead; + bufferOut += excessSamplesRead; + samplesToRead -= excessSamplesRead; + } + } } return samplesRead; @@ -5068,24 +5050,24 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut) { - // This reads samples in 2 passes and can probably be optimized. + /* This reads samples in 2 passes and can probably be optimized. */ drflac_uint64 totalSamplesRead = 0; - while (samplesToRead > 0) { - drflac_int32 samples32[4096]; - drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32); - if (samplesJustRead == 0) { - break; // Reached the end. - } + while (samplesToRead > 0) + { + drflac_uint64 i; + drflac_int32 samples32[4096]; + drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32); + if (samplesJustRead == 0) + break; /* Reached the end. */ - // s32 -> s16 - for (drflac_uint64 i = 0; i < samplesJustRead; ++i) { - pBufferOut[i] = (drflac_int16)(samples32[i] >> 16); - } + /* s32 -> s16 */ + for (i = 0; i < samplesJustRead; ++i) + pBufferOut[i] = (drflac_int16)(samples32[i] >> 16); - totalSamplesRead += samplesJustRead; - samplesToRead -= samplesJustRead; - pBufferOut += samplesJustRead; + totalSamplesRead += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; } return totalSamplesRead; @@ -5093,20 +5075,20 @@ drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut) { - // This reads samples in 2 passes and can probably be optimized. + /* This reads samples in 2 passes and can probably be optimized. */ drflac_uint64 totalSamplesRead = 0; - while (samplesToRead > 0) { + while (samplesToRead > 0) + { + drflac_uint64 i; drflac_int32 samples32[4096]; drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32); - if (samplesJustRead == 0) { - break; // Reached the end. - } + if (samplesJustRead == 0) + break; /* Reached the end. */ - // s32 -> f32 - for (drflac_uint64 i = 0; i < samplesJustRead; ++i) { + /* s32 -> f32 */ + for (i = 0; i < samplesJustRead; ++i) pBufferOut[i] = (float)(samples32[i] / 2147483648.0); - } totalSamplesRead += samplesJustRead; samplesToRead -= samplesJustRead; @@ -5118,40 +5100,33 @@ drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) { - if (pFlac == NULL) { + if (pFlac == NULL) return DRFLAC_FALSE; - } - // If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present - // when the decoder was opened. - if (pFlac->firstFramePos == 0) { + /* If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present + * when the decoder was opened. */ + if (pFlac->firstFramePos == 0) return DRFLAC_FALSE; - } - if (sampleIndex == 0) { + if (sampleIndex == 0) return drflac__seek_to_first_frame(pFlac); - } - // Clamp the sample to the end. - if (sampleIndex >= pFlac->totalSampleCount) { + /* Clamp the sample to the end. */ + if (sampleIndex >= pFlac->totalSampleCount) sampleIndex = pFlac->totalSampleCount - 1; - } - // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so - // we'll instead use Ogg's natural seeking facility. + /* Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so + * we'll instead use Ogg's natural seeking facility. */ #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) - { - return drflac_ogg__seek_to_sample(pFlac, sampleIndex); - } + return drflac_ogg__seek_to_sample(pFlac, sampleIndex); else #endif { - // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. - if (!drflac__seek_to_sample__seek_table(pFlac, sampleIndex)) { + /* First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. */ + if (!drflac__seek_to_sample__seek_table(pFlac, sampleIndex)) return drflac__seek_to_sample__brute_force(pFlac, sampleIndex); - } } @@ -5241,133 +5216,134 @@ DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(f32, float) drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { - /* Safety. */ - if (sampleRate) *sampleRate = 0; - if (channels) *channels = 0; - if (totalSampleCount) *totalSampleCount = 0; + drflac* pFlac; + /* Safety. */ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; - drflac* pFlac = drflac_open(onRead, onSeek, pUserData); - if (pFlac == NULL) { - return NULL; - } + pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) + return NULL; - return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); } drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { - /* Safety. */ - if (sampleRate) *sampleRate = 0; - if (channels) *channels = 0; - if (totalSampleCount) *totalSampleCount = 0; + drflac* pFlac; + /* Safety. */ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; - drflac* pFlac = drflac_open(onRead, onSeek, pUserData); - if (pFlac == NULL) { - return NULL; - } + pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) + return NULL; - return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); } float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { - /* Safety. */ - if (sampleRate) *sampleRate = 0; - if (channels) *channels = 0; - if (totalSampleCount) *totalSampleCount = 0; + drflac* pFlac; + /* Safety. */ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; - drflac* pFlac = drflac_open(onRead, onSeek, pUserData); - if (pFlac == NULL) { - return NULL; - } + pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) + return NULL; - return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); } #ifndef DR_FLAC_NO_STDIO drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { - if (sampleRate) *sampleRate = 0; - if (channels) *channels = 0; - if (totalSampleCount) *totalSampleCount = 0; + drflac* pFlac; + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; - drflac* pFlac = drflac_open_file(filename); - if (pFlac == NULL) { - return NULL; - } + pFlac = drflac_open_file(filename); + if (pFlac == NULL) + return NULL; - return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); } drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { - if (sampleRate) *sampleRate = 0; - if (channels) *channels = 0; - if (totalSampleCount) *totalSampleCount = 0; + drflac* pFlac; + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; - drflac* pFlac = drflac_open_file(filename); - if (pFlac == NULL) { - return NULL; - } + pFlac = drflac_open_file(filename); + if (pFlac == NULL) + return NULL; - return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); } float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { - if (sampleRate) *sampleRate = 0; - if (channels) *channels = 0; - if (totalSampleCount) *totalSampleCount = 0; + drflac* pFlac; + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; - drflac* pFlac = drflac_open_file(filename); - if (pFlac == NULL) { - return NULL; - } + pFlac = drflac_open_file(filename); + if (pFlac == NULL) + return NULL; - return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); } #endif drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { - if (sampleRate) *sampleRate = 0; - if (channels) *channels = 0; - if (totalSampleCount) *totalSampleCount = 0; + drflac *pFlac; - drflac* pFlac = drflac_open_memory(data, dataSize); - if (pFlac == NULL) { - return NULL; - } + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; - return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); + pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) + return NULL; + + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); } drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { - if (sampleRate) *sampleRate = 0; - if (channels) *channels = 0; - if (totalSampleCount) *totalSampleCount = 0; + drflac* pFlac; + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; - drflac* pFlac = drflac_open_memory(data, dataSize); - if (pFlac == NULL) { - return NULL; - } + pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) + return NULL; - return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); } float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { - if (sampleRate) *sampleRate = 0; - if (channels) *channels = 0; - if (totalSampleCount) *totalSampleCount = 0; + drflac* pFlac; + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; - drflac* pFlac = drflac_open_memory(data, dataSize); - if (pFlac == NULL) { - return NULL; - } + pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) + return NULL; - return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); } void drflac_free(void* pSampleDataReturnedByOpenAndDecode) @@ -5380,9 +5356,8 @@ void drflac_free(void* pSampleDataReturnedByOpenAndDecode) void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments) { - if (pIter == NULL) { + if (pIter == NULL) return; - } pIter->countRemaining = commentCount; pIter->pRunningData = pComments; @@ -5390,18 +5365,19 @@ void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) { + const char* pComment; + drflac_uint32 length; /* Safety. */ if (pCommentLengthOut) *pCommentLengthOut = 0; - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) return NULL; - } - drflac_uint32 length = drflac__le2host_32(*(drflac_uint32*)pIter->pRunningData); - pIter->pRunningData += 4; + length = drflac__le2host_32(*(drflac_uint32*)pIter->pRunningData); + pIter->pRunningData += 4; - const char* pComment = pIter->pRunningData; - pIter->pRunningData += length; + pComment = pIter->pRunningData; + pIter->pRunningData += length; pIter->countRemaining -= 1; if (pCommentLengthOut) *pCommentLengthOut = length; From 89d765d6c7bb54d6e4bd49ab7aa6de82988404d3 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 12:44:25 +0200 Subject: [PATCH 504/517] Define DR_FLAC_NO_STDIO --- deps/dr/dr_flac.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deps/dr/dr_flac.h b/deps/dr/dr_flac.h index 6e3ac33022..a0f91ae801 100644 --- a/deps/dr/dr_flac.h +++ b/deps/dr/dr_flac.h @@ -1,6 +1,8 @@ #ifndef dr_flac_h #define dr_flac_h +#define DR_FLAC_NO_STDIO + #include #include From 9a6e069d6fadddf4cd197f5aa222968d8689b67b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 12:44:52 +0200 Subject: [PATCH 505/517] (dr_flac.h) Assume stdint.h presence --- deps/dr/dr_flac.h | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/deps/dr/dr_flac.h b/deps/dr/dr_flac.h index a0f91ae801..e8586f56e1 100644 --- a/deps/dr/dr_flac.h +++ b/deps/dr/dr_flac.h @@ -3,20 +3,10 @@ #define DR_FLAC_NO_STDIO +#include #include #include -#if defined(_MSC_VER) && _MSC_VER < 1600 -typedef signed char drflac_int8; -typedef unsigned char drflac_uint8; -typedef signed short drflac_int16; -typedef unsigned short drflac_uint16; -typedef signed int drflac_int32; -typedef unsigned int drflac_uint32; -typedef signed __int64 drflac_int64; -typedef unsigned __int64 drflac_uint64; -#else -#include typedef int8_t drflac_int8; typedef uint8_t drflac_uint8; typedef int16_t drflac_int16; @@ -25,7 +15,6 @@ typedef int32_t drflac_int32; typedef uint32_t drflac_uint32; typedef int64_t drflac_int64; typedef uint64_t drflac_uint64; -#endif typedef drflac_uint8 drflac_bool8; typedef drflac_uint32 drflac_bool32; #define DRFLAC_TRUE 1 From 381f14fa9a52f342c751373656ad4b90112ba0fd Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 13:13:22 +0200 Subject: [PATCH 506/517] (dr_flac.h) Silence warnings --- deps/dr/dr_flac.h | 797 +++++++++++++++++++++++++--------------------- 1 file changed, 428 insertions(+), 369 deletions(-) diff --git a/deps/dr/dr_flac.h b/deps/dr/dr_flac.h index e8586f56e1..dddba62b83 100644 --- a/deps/dr/dr_flac.h +++ b/deps/dr/dr_flac.h @@ -989,12 +989,12 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) { - drflac_assert(count <= 32); #ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; + drflac_assert(count <= 32); return 0; #else #if 0 @@ -1010,13 +1010,13 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 da } return crc; #else - drflac_uint32 wholeBytes = count >> 3; - drflac_uint32 leftoverBits = count - (wholeBytes*8); - static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + drflac_assert(count <= 32); switch (wholeBytes) { @@ -1057,12 +1057,12 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) { - drflac_assert(count <= 64); #ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; + drflac_assert(count <= 64); return 0; #else #if 0 @@ -1087,6 +1087,8 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac }; drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + drflac_assert(count <= 64); + switch (wholeBytes) { default: @@ -1103,12 +1105,12 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) { - drflac_assert(count <= 64); #ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; + drflac_assert(count <= 64); return 0; #else drflac_uint32 wholeBytes = count >> 3; @@ -1119,6 +1121,8 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac }; drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + drflac_assert(count <= 64); + switch (wholeBytes) { default: @@ -1270,6 +1274,8 @@ static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs static drflac_bool32 drflac__reload_cache(drflac_bs* bs) { + size_t bytesRead; + #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif @@ -1290,7 +1296,7 @@ static drflac_bool32 drflac__reload_cache(drflac_bs* bs) /* If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last * few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the * data from the unaligned cache. */ - size_t bytesRead = bs->unalignedByteCount; + bytesRead = bs->unalignedByteCount; if (bytesRead == 0) return DRFLAC_FALSE; @@ -1367,20 +1373,22 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) { - drflac_assert(bs != NULL); - drflac_assert(pResult != NULL); - drflac_assert(bitCount > 0); - drflac_assert(bitCount <= 32); + drflac_uint32 result; + drflac_uint32 signbit; - drflac_uint32 result; - if (!drflac__read_uint32(bs, bitCount, &result)) - return DRFLAC_FALSE; + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 32); - drflac_uint32 signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; + if (!drflac__read_uint32(bs, bitCount, &result)) + return DRFLAC_FALSE; - *pResult = (drflac_int32)result; - return DRFLAC_TRUE; + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + + *pResult = (drflac_int32)result; + return DRFLAC_TRUE; } static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) @@ -1554,26 +1562,27 @@ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) for (;;) { + drflac_uint8 hi; + #ifndef DR_FLAC_NO_CRC - drflac__reset_crc16(bs); + drflac__reset_crc16(bs); #endif - drflac_uint8 hi; - if (!drflac__read_uint8(bs, 8, &hi)) - return DRFLAC_FALSE; + if (!drflac__read_uint8(bs, 8, &hi)) + return DRFLAC_FALSE; - if (hi == 0xFF) - { - drflac_uint8 lo; - if (!drflac__read_uint8(bs, 6, &lo)) - return DRFLAC_FALSE; + if (hi == 0xFF) + { + drflac_uint8 lo; + if (!drflac__read_uint8(bs, 6, &lo)) + return DRFLAC_FALSE; - if (lo == 0x3E) - return DRFLAC_TRUE; + if (lo == 0x3E) + return DRFLAC_TRUE; - if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) - return DRFLAC_FALSE; - } + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) + return DRFLAC_FALSE; + } } #if 0 @@ -1683,23 +1692,24 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) static INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) { - drflac_uint32 zeroCounter = 0; - while (bs->cache == 0) - { - zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) - return DRFLAC_FALSE; - } + drflac_uint32 setBitOffsetPlus1; + drflac_uint32 zeroCounter = 0; + while (bs->cache == 0) + { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) + return DRFLAC_FALSE; + } - drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); - zeroCounter += setBitOffsetPlus1; - setBitOffsetPlus1 += 1; + setBitOffsetPlus1 = drflac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; - bs->consumedBits += setBitOffsetPlus1; - bs->cache <<= setBitOffsetPlus1; + bs->consumedBits += setBitOffsetPlus1; + bs->cache <<= setBitOffsetPlus1; - *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return DRFLAC_TRUE; + *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; + return DRFLAC_TRUE; } @@ -1746,14 +1756,16 @@ static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFro static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) { + drflac_uint64 result; int i; int byteCount = 1; unsigned char utf8[7] = {0}; + drflac_uint8 crc; drflac_assert(bs != NULL); drflac_assert(pNumberOut != NULL); - drflac_uint8 crc = *pCRCOut; + crc = *pCRCOut; if (!drflac__read_uint8(bs, 8, utf8)) { @@ -1790,7 +1802,7 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 /* Read extra bytes. */ drflac_assert(byteCount > 1); - drflac_uint64 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); for (i = 1; i < byteCount; ++i) { if (!drflac__read_uint8(bs, 8, utf8 + i)) @@ -2126,79 +2138,82 @@ static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_ui static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) { - drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); - drflac_cache_t resultHiShift = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParam; + drflac_uint32 riceLength; + drflac_uint32 riceParamPart; + drflac_uint32 setBitOffsetPlus1; + drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); + drflac_cache_t resultHiShift = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParam; + drflac_uint32 zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) + return DRFLAC_FALSE; + } + setBitOffsetPlus1 = drflac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; - drflac_uint32 zeroCounter = 0; - while (bs->cache == 0) { - zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) + riceLength = setBitOffsetPlus1 + riceParam; + + if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) + { + riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> (DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceLength)); + + bs->consumedBits += riceLength; + bs->cache <<= riceLength; + } + else + { + drflac_uint32 bitCountLo; + drflac_cache_t resultHi; + + bs->consumedBits += riceLength; + if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) + bs->cache <<= setBitOffsetPlus1; + + /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ + bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); + resultHi = bs->cache & riceParamMask; /* <-- This mask is OK because all bits after the first bits are always zero. */ + + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); +#endif + bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->consumedBits = 0; +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; - } + } + } - drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); - zeroCounter += setBitOffsetPlus1; - setBitOffsetPlus1 += 1; + riceParamPart = (drflac_uint32)((resultHi >> resultHiShift) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo)); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + } - drflac_uint32 riceParamPart; - drflac_uint32 riceLength = setBitOffsetPlus1 + riceParam; - if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> (DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceLength)); - - bs->consumedBits += riceLength; - bs->cache <<= riceLength; - } else { - bs->consumedBits += riceLength; - if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - bs->cache <<= setBitOffsetPlus1; - } - - /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ - drflac_uint32 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); - drflac_cache_t resultHi = bs->cache & riceParamMask; /* <-- This mask is OK because all bits after the first bits are always zero. */ - - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); - #endif - bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs->consumedBits = 0; - #ifndef DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; - #endif - } else { - /* Slow path. We need to fetch more data from the client. */ - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - } - - riceParamPart = (drflac_uint32)((resultHi >> resultHiShift) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo)); - - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - } - - *pZeroCounterOut = zeroCounter; - *pRiceParamPartOut = riceParamPart; - return DRFLAC_TRUE; + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = riceParamPart; + return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { + drflac_uint32 zeroCountPart; + drflac_uint32 riceParamPart; drflac_uint32 i = 0; drflac_assert(bs != NULL); drflac_assert(count > 0); drflac_assert(pSamplesOut != NULL); - drflac_uint32 zeroCountPart; - drflac_uint32 riceParamPart; - - while (i < count) { static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; @@ -2291,6 +2306,8 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ { drflac_uint8 partitionOrder; drflac_uint8 residualMethod; + drflac_uint32 samplesInPartition; + drflac_uint32 partitionsRemaining; drflac_assert(bs != NULL); drflac_assert(blockSize != 0); @@ -2318,8 +2335,9 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ if ((blockSize / (1 << partitionOrder)) <= order) return DRFLAC_FALSE; - drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - drflac_uint32 partitionsRemaining = (1 << partitionOrder); + samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + partitionsRemaining = (1 << partitionOrder); + for (;;) { drflac_uint8 riceParam = 0; @@ -2373,6 +2391,8 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ * and parameters are used to determine how many residual values need to be decoded. */ static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) { + drflac_uint32 partitionsRemaining; + drflac_uint32 samplesInPartition; drflac_uint8 partitionOrder; drflac_uint8 residualMethod; @@ -2388,8 +2408,8 @@ static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 if (!drflac__read_uint8(bs, 4, &partitionOrder)) return DRFLAC_FALSE; - drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - drflac_uint32 partitionsRemaining = (1 << partitionOrder); + samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + partitionsRemaining = (1 << partitionOrder); for (;;) { drflac_uint8 riceParam = 0; @@ -2534,12 +2554,12 @@ static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 bl static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) { - drflac_assert(bs != NULL); - drflac_assert(header != NULL); - const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; /* -1 = reserved. */ + drflac_assert(bs != NULL); + drflac_assert(header != NULL); + /* Keep looping until we find a valid sync code. */ for (;;) { @@ -2779,135 +2799,143 @@ static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) { - drflac_assert(bs != NULL); - drflac_assert(frame != NULL); + drflac_subframe *pSubframe; - drflac_subframe* pSubframe = frame->subframes + subframeIndex; - if (!drflac__read_subframe_header(bs, pSubframe)) - return DRFLAC_FALSE; + drflac_assert(bs != NULL); + drflac_assert(frame != NULL); - /* Side channels require an extra bit per sample. Took a while to figure that one out... */ - pSubframe->bitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - pSubframe->bitsPerSample += 1; - } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - pSubframe->bitsPerSample += 1; - } + pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) + return DRFLAC_FALSE; - /* Need to handle wasted bits per sample. */ - pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pDecodedSamples = NULL; + /* Side channels require an extra bit per sample. Took a while to figure that one out... */ + pSubframe->bitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + pSubframe->bitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + pSubframe->bitsPerSample += 1; + } - switch (pSubframe->subframeType) - { - case DRFLAC_SUBFRAME_CONSTANT: - { + /* Need to handle wasted bits per sample. */ + pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pDecodedSamples = NULL; + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) - return DRFLAC_FALSE; - } break; + return DRFLAC_FALSE; + } break; - case DRFLAC_SUBFRAME_VERBATIM: - { + case DRFLAC_SUBFRAME_VERBATIM: + { unsigned int bitsToSeek = frame->header.blockSize * pSubframe->bitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) - return DRFLAC_FALSE; - } break; + return DRFLAC_FALSE; + } break; - case DRFLAC_SUBFRAME_FIXED: - { + case DRFLAC_SUBFRAME_FIXED: + { unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) - return DRFLAC_FALSE; + return DRFLAC_FALSE; if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) - return DRFLAC_FALSE; - } break; + return DRFLAC_FALSE; + } break; - case DRFLAC_SUBFRAME_LPC: - { + case DRFLAC_SUBFRAME_LPC: + { unsigned char lpcPrecision; unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) - return DRFLAC_FALSE; + return DRFLAC_FALSE; if (!drflac__read_uint8(bs, 4, &lpcPrecision)) - return DRFLAC_FALSE; + return DRFLAC_FALSE; if (lpcPrecision == 15) - return DRFLAC_FALSE; /* Invalid. */ + return DRFLAC_FALSE; /* Invalid. */ lpcPrecision += 1; bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; /* +5 for shift. */ if (!drflac__seek_bits(bs, bitsToSeek)) - return DRFLAC_FALSE; + return DRFLAC_FALSE; if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) - return DRFLAC_FALSE; - } break; + return DRFLAC_FALSE; + } break; - default: return DRFLAC_FALSE; - } + default: return DRFLAC_FALSE; + } - return DRFLAC_TRUE; + return DRFLAC_TRUE; } static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) { - drflac_assert(channelAssignment <= 10); - drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + + drflac_assert(channelAssignment <= 10); return lookup[channelAssignment]; } static drflac_result drflac__decode_frame(drflac* pFlac) { + drflac_uint16 actualCRC16; + drflac_uint8 paddingSizeInBits; + drflac_uint16 desiredCRC16; int i, channelCount; - /* This function should be called while the stream is sitting on the first byte after the frame header. */ - drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes)); - /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */ - if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) - return DRFLAC_ERROR; + /* This function should be called while the stream is sitting on the first byte after the frame header. */ + drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes)); - /* The number of channels in the frame must match the channel count from the STREAMINFO block. */ - channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - if (channelCount != (int)pFlac->channels) - return DRFLAC_ERROR; + /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */ + if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) + return DRFLAC_ERROR; - for (i = 0; i < channelCount; ++i) - { - if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) - return DRFLAC_ERROR; - } + /* The number of channels in the frame must match the channel count from the STREAMINFO block. */ + channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + if (channelCount != (int)pFlac->channels) + return DRFLAC_ERROR; - drflac_uint8 paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7; - if (paddingSizeInBits > 0) - { - drflac_uint8 padding = 0; - if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) - return DRFLAC_END_OF_STREAM; - } + for (i = 0; i < channelCount; ++i) + { + if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) + return DRFLAC_ERROR; + } + + paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7; + if (paddingSizeInBits > 0) + { + drflac_uint8 padding = 0; + if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) + return DRFLAC_END_OF_STREAM; + } #ifndef DR_FLAC_NO_CRC - drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); + actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif - drflac_uint16 desiredCRC16; - if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) - return DRFLAC_END_OF_STREAM; + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) + return DRFLAC_END_OF_STREAM; #ifndef DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) - return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ + if (actualCRC16 != desiredCRC16) + return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ #endif - pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount; + pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount; - return DRFLAC_SUCCESS; + return DRFLAC_SUCCESS; } static drflac_result drflac__seek_frame(drflac* pFlac) { +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16; +#endif drflac_uint16 desiredCRC16; int i; int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); @@ -2924,7 +2952,7 @@ static drflac_result drflac__seek_frame(drflac* pFlac) /* CRC. */ #ifndef DR_FLAC_NO_CRC - drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); + actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) return DRFLAC_END_OF_STREAM; @@ -2943,10 +2971,12 @@ static drflac_bool32 drflac__read_and_decode_next_frame(drflac* pFlac) for (;;) { + drflac_result result; + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) return DRFLAC_FALSE; - drflac_result result = drflac__decode_frame(pFlac); + result = drflac__decode_frame(pFlac); if (result != DRFLAC_SUCCESS) { if (result == DRFLAC_CRC_MISMATCH) @@ -2984,12 +3014,14 @@ static void drflac__get_current_frame_sample_range(drflac* pFlac, drflac_uint64* static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) { - drflac_assert(pFlac != NULL); + drflac_bool32 result; - drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); + drflac_assert(pFlac != NULL); - drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame)); - return result; + result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); + + drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame)); + return result; } static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac) @@ -3010,6 +3042,7 @@ static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_u for (;;) { + drflac_uint64 sampleCountInThisFrame; drflac_uint64 firstSampleInFrame = 0; drflac_uint64 lastSampleInFrame = 0; @@ -3018,7 +3051,7 @@ static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_u drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); - drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { /* The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend @@ -3059,6 +3092,11 @@ static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_u static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_uint64 sampleIndex) { + drflac_seekpoint closestSeekpoint = {0, 0, 0}; + drflac_uint32 seekpointCount; + drflac_uint32 seekpointsRemaining; + drflac_uint64 runningSampleCount; + drflac_assert(pFlac != NULL); if (pFlac->seektablePos == 0) @@ -3068,29 +3106,28 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui return DRFLAC_FALSE; /* The number of seek points is derived from the size of the SEEKTABLE block. */ - drflac_uint32 seekpointCount = pFlac->seektableSize / 18; /* 18 = the size of each seek point. */ + seekpointCount = pFlac->seektableSize / 18; /* 18 = the size of each seek point. */ if (seekpointCount == 0) return DRFLAC_FALSE; /* Would this ever happen? */ - drflac_seekpoint closestSeekpoint = {0, 0, 0}; + seekpointsRemaining = seekpointCount; + while (seekpointsRemaining > 0) + { + drflac_seekpoint seekpoint; + if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) + break; + if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) + break; + if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) + break; - drflac_uint32 seekpointsRemaining = seekpointCount; - while (seekpointsRemaining > 0) { - drflac_seekpoint seekpoint; - if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) - break; - if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) - break; - if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) - break; + /* Note that the seekpoint sample is based on a single channel. The input sample (sampleIndex) is based on interleaving, thus + * we need to multiple the seekpoint's sample by the channel count. */ + if (seekpoint.firstSample*pFlac->channels > sampleIndex) + break; - /* Note that the seekpoint sample is based on a single channel. The input sample (sampleIndex) is based on interleaving, thus - * we need to multiple the seekpoint's sample by the channel count. */ - if (seekpoint.firstSample*pFlac->channels > sampleIndex) - break; - - closestSeekpoint = seekpoint; - seekpointsRemaining -= 1; + closestSeekpoint = seekpoint; + seekpointsRemaining -= 1; } /* At this point we should have found the seekpoint closest to our sample. We need to seek to it using basically the same @@ -3098,9 +3135,10 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + closestSeekpoint.frameOffset)) return DRFLAC_FALSE; - drflac_uint64 runningSampleCount = closestSeekpoint.firstSample*pFlac->channels; + runningSampleCount = closestSeekpoint.firstSample*pFlac->channels; for (;;) { + drflac_uint64 sampleCountInThisFrame; drflac_uint64 firstSampleInFrame = 0; drflac_uint64 lastSampleInFrame = 0; @@ -3109,7 +3147,7 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); - drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { /* The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend @@ -3251,25 +3289,26 @@ drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) { - drflac_assert(pFlac != NULL); - /* We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that * we'll be sitting on byte 42. */ drflac_uint64 runningFilePos = 42; drflac_uint64 seektablePos = 0; drflac_uint32 seektableSize = 0; + drflac_assert(pFlac != NULL); + for (;;) { + drflac_metadata metadata; drflac_uint8 isLastBlock = 0; drflac_uint8 blockType; drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(pFlac->bs.onRead, pFlac->bs.pUserData, &isLastBlock, &blockType, &blockSize)) return DRFLAC_FALSE; runningFilePos += 4; - drflac_metadata metadata; metadata.type = blockType; metadata.pRawData = NULL; metadata.rawDataSize = 0; @@ -3339,6 +3378,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) { if (pFlac->onMeta) { + const char* pRunningData; void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) return DRFLAC_FALSE; @@ -3351,7 +3391,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; - const char* pRunningData = (const char*)pRawData; + pRunningData = (const char*)pRawData; metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; @@ -3366,6 +3406,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) { if (pFlac->onMeta) { + const char* pRunningData; void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) return DRFLAC_FALSE; @@ -3378,7 +3419,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; - const char* pRunningData = (const char*)pRawData; + pRunningData = (const char*)pRawData; drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(drflac_uint64*)pRunningData); pRunningData += 4; metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; @@ -3394,6 +3435,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) { if (pFlac->onMeta) { + const char* pRunningData; void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) return DRFLAC_FALSE; @@ -3406,7 +3448,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; - const char* pRunningData = (const char*)pRawData; + pRunningData = (const char*)pRawData; metadata.data.picture.type = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.picture.mimeLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; @@ -3750,9 +3792,10 @@ drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserD { if (drflac_ogg__is_capture_pattern(id)) { + drflac_result result; *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; - drflac_result result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); if (result == DRFLAC_SUCCESS) return DRFLAC_SUCCESS; @@ -3845,13 +3888,17 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og { drflac_ogg_page_header header; for (;;) { +#ifndef DR_FLAC_NO_CRC + drflac_uint32 actualCRC32; +#endif + drflac_uint32 pageBodySize; drflac_uint32 crc32 = 0; drflac_uint32 bytesRead; if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) return DRFLAC_FALSE; oggbs->currentBytePos += bytesRead; - drflac_uint32 pageBodySize = drflac_ogg__get_page_body_size(&header); + pageBodySize = drflac_ogg__get_page_body_size(&header); if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) continue; /* Invalid page size. Assume it's corrupted and just move to the next page. */ @@ -3869,7 +3916,7 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og oggbs->pageDataSize = pageBodySize; #ifndef DR_FLAC_NO_CRC - drflac_uint32 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); + actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); if (actualCRC32 != header.checksum) { if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) @@ -4058,121 +4105,125 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) { - drflac_uint64 runningGranulePosition = 0; - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + drflac_uint64 runningSampleCount; + drflac_uint64 runningFrameBytePos; + drflac_uint64 runningGranulePosition = 0; + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - drflac_uint64 originalBytePos = oggbs->currentBytePos; /* For recovery. */ + drflac_uint64 originalBytePos = oggbs->currentBytePos; /* For recovery. */ - /* First seek to the first frame. */ - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) - return DRFLAC_FALSE; - oggbs->bytesRemainingInPage = 0; + /* First seek to the first frame. */ + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) + return DRFLAC_FALSE; + oggbs->bytesRemainingInPage = 0; - drflac_uint64 runningFrameBytePos = oggbs->currentBytePos; /* <-- Points to the OggS identifier. */ - for (;;) { - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { - drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); - return DRFLAC_FALSE; /* Never did find that sample... */ - } + runningFrameBytePos = oggbs->currentBytePos; /* <-- Points to the OggS identifier. */ + for (;;) + { + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DRFLAC_FALSE; /* Never did find that sample... */ + } - runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; - if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) - break; /* The sample is somewhere in the previous page. */ + runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) + break; /* The sample is somewhere in the previous page. */ - /* At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we - * disregard any pages that do not begin a fresh packet. */ - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) - { /* <-- Is it a fresh page? */ - if (oggbs->currentPageHeader.segmentTable[0] >= 2) - { - drflac_uint8 firstBytesInPage[2]; - firstBytesInPage[0] = oggbs->pageData[0]; - firstBytesInPage[1] = oggbs->pageData[1]; + /* At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we + * disregard any pages that do not begin a fresh packet. */ + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) + { /* <-- Is it a fresh page? */ + if (oggbs->currentPageHeader.segmentTable[0] >= 2) + { + drflac_uint8 firstBytesInPage[2]; + firstBytesInPage[0] = oggbs->pageData[0]; + firstBytesInPage[1] = oggbs->pageData[1]; - if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) /* <-- Does the page begin with a frame's sync code? */ - runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels; + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) /* <-- Does the page begin with a frame's sync code? */ + runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels; - continue; - } - } - } + continue; + } + } + } - /* We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the - * start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of - * a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until - * we find the one containing the target sample. */ - if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) - return DRFLAC_FALSE; - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) - return DRFLAC_FALSE; + /* We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the + * start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of + * a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until + * we find the one containing the target sample. */ + if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) + return DRFLAC_FALSE; + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) + return DRFLAC_FALSE; - /* At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep - * looping over these frames until we find the one containing the sample we're after. */ - drflac_uint64 runningSampleCount = runningGranulePosition; - for (;;) - { - /* There are two ways to find the sample and seek past irrelevant frames: - * 1) Use the native FLAC decoder. - * 2) Use Ogg's framing system. - * - * Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to - * do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code - * duplication for the decoding of frame headers. - * - * Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg - * bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the - * standard drflac__*() APIs because that will read in extra data for it's own internal caching which in turn breaks - * the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read - * using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to - * avoid the use of the drflac_bs object. - * - * Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: - * 1) Seeking is already partially accellerated using Ogg's paging system in the code block above. - * 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. - * 3) Simplicity. - */ - if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) - return DRFLAC_FALSE; + /* At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep + * looping over these frames until we find the one containing the sample we're after. */ + runningSampleCount = runningGranulePosition; + for (;;) + { + drflac_uint64 sampleCountInThisFrame; + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + /* There are two ways to find the sample and seek past irrelevant frames: + * 1) Use the native FLAC decoder. + * 2) Use Ogg's framing system. + * + * Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to + * do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code + * duplication for the decoding of frame headers. + * + * Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg + * bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the + * standard drflac__*() APIs because that will read in extra data for it's own internal caching which in turn breaks + * the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read + * using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to + * avoid the use of the drflac_bs object. + * + * Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: + * 1) Seeking is already partially accellerated using Ogg's paging system in the code block above. + * 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. + * 3) Simplicity. + */ + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) + return DRFLAC_FALSE; - drflac_uint64 firstSampleInFrame = 0; - drflac_uint64 lastSampleInFrame = 0; - drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); - drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; - if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { - /* The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend - * it never existed and keep iterating. */ - drflac_result result = drflac__decode_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ - drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */ - if (samplesToDecode == 0) - return DRFLAC_TRUE; - return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; /* <-- If this fails, something bad has happened (it should never fail). */ - } - else - { - if (result == DRFLAC_CRC_MISMATCH) - continue; /* CRC mismatch. Pretend this frame never existed. */ - return DRFLAC_FALSE; - } - } - else - { - /* It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this - * frame never existed and leave the running sample count untouched. */ - drflac_result result = drflac__seek_to_next_frame(pFlac); - if (result == DRFLAC_SUCCESS) - runningSampleCount += sampleCountInThisFrame; - else - { - if (result == DRFLAC_CRC_MISMATCH) - continue; /* CRC mismatch. Pretend this frame never existed. */ - return DRFLAC_FALSE; - } - } - } + sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + /* The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + * it never existed and keep iterating. */ + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */ + if (samplesToDecode == 0) + return DRFLAC_TRUE; + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; /* <-- If this fails, something bad has happened (it should never fail). */ + } + else + { + if (result == DRFLAC_CRC_MISMATCH) + continue; /* CRC mismatch. Pretend this frame never existed. */ + return DRFLAC_FALSE; + } + } + else + { + /* It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + * frame never existed and leave the running sample count untouched. */ + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) + runningSampleCount += sampleCountInThisFrame; + else + { + if (result == DRFLAC_CRC_MISMATCH) + continue; /* CRC mismatch. Pretend this frame never existed. */ + return DRFLAC_FALSE; + } + } + } } @@ -4243,6 +4294,7 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { + drflac_streaminfo streaminfo; /* The remaining data in the page should be the STREAMINFO block. */ drflac_uint8 isLastBlock; drflac_uint8 blockType; @@ -4253,7 +4305,6 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) return DRFLAC_FALSE; /* Invalid block type. First block must be the STREAMINFO block. */ - drflac_streaminfo streaminfo; if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { /* Success! */ pInit->hasStreamInfoBlock = DRFLAC_TRUE; @@ -4331,6 +4382,7 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { drflac_uint8 id[4]; + drflac_bool32 relaxed; if (pInit == NULL || onRead == NULL || onSeek == NULL) return DRFLAC_FALSE; @@ -4350,7 +4402,7 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR /* If the container is explicitly defined then we can try opening in relaxed mode. */ - drflac_bool32 relaxed = container != drflac_container_unknown; + relaxed = container != drflac_container_unknown; /* Skip over any ID3 tags. */ for (;;) @@ -4427,6 +4479,10 @@ void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit) drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { drflac_init_info init; + drflac* pFlac; + drflac_uint32 decodedSamplesAllocationSize; + drflac_uint32 allocationSize; + drflac_uint32 wholeSIMDVectorCountPerChannel; #ifndef DRFLAC_NO_CPUID /* CPU support first. */ @@ -4444,18 +4500,17 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p * The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration * the different SIMD instruction sets. */ - drflac_uint32 allocationSize = sizeof(drflac); + allocationSize = sizeof(drflac); /* The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector * we are supporting. */ - drflac_uint32 wholeSIMDVectorCountPerChannel; if ((init.maxBlockSize % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); } else { wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; } - drflac_uint32 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; allocationSize += decodedSamplesAllocationSize; allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; /* Allocate extra bytes to ensure we have enough for alignment. */ @@ -4466,7 +4521,7 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p allocationSize += sizeof(drflac_oggbs); #endif - drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize); + pFlac = (drflac*)DRFLAC_MALLOC(allocationSize); drflac__init_from_info(pFlac, &init); pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); @@ -4641,21 +4696,22 @@ drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc on static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { - drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; - drflac_assert(memoryStream != NULL); - drflac_assert(memoryStream->dataSize >= memoryStream->currentReadPos); + size_t bytesRemaining; + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + drflac_assert(memoryStream != NULL); + drflac_assert(memoryStream->dataSize >= memoryStream->currentReadPos); - size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } + bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; + if (bytesToRead > bytesRemaining) + bytesToRead = bytesRemaining; - if (bytesToRead > 0) { - drflac_copy_memory(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); - memoryStream->currentReadPos += bytesToRead; - } + if (bytesToRead > 0) + { + drflac_copy_memory(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + memoryStream->currentReadPos += bytesToRead; + } - return bytesToRead; + return bytesToRead; } static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) @@ -4684,10 +4740,12 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_ drflac* drflac_open_memory(const void* data, size_t dataSize) { drflac__memory_stream memoryStream; + drflac* pFlac; + memoryStream.data = (const unsigned char*)data; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - drflac* pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream); + pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream); if (pFlac == NULL) return NULL; @@ -4712,10 +4770,13 @@ drflac* drflac_open_memory(const void* data, size_t dataSize) drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData) { drflac__memory_stream memoryStream; + drflac* pFlac; + memoryStream.data = (const unsigned char*)data; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData); + + pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData); if (pFlac == NULL) return NULL; @@ -4907,14 +4968,17 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac } else { + drflac_uint64 alignedSamplesRead; + drflac_uint64 alignedSampleCountPerChannel; + drflac_uint64 firstAlignedSampleInFrame; + unsigned int unusedBitsPerSample; /* Here is where we grab the samples and interleave them. */ - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; - drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; - if (misalignedSampleCount > 0) { + if (misalignedSampleCount > 0) + { drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); samplesRead += misalignedSamplesRead; samplesReadFromFrameSoFar += misalignedSamplesRead; @@ -4922,14 +4986,12 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac samplesToRead -= misalignedSamplesRead; } - - drflac_uint64 alignedSampleCountPerChannel = samplesToRead / channelCount; - if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) { + alignedSampleCountPerChannel = samplesToRead / channelCount; + if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount; - } - drflac_uint64 firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount; - unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample; + firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount; + unusedBitsPerSample = 32 - pFlac->bitsPerSample; switch (pFlac->currentFrame.header.channelAssignment) { @@ -5012,7 +5074,7 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac } break; } - drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount; + alignedSamplesRead = alignedSampleCountPerChannel * channelCount; samplesRead += alignedSamplesRead; samplesReadFromFrameSoFar += alignedSamplesRead; bufferOut += alignedSamplesRead; @@ -5140,26 +5202,23 @@ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) /* Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me. */ #define DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(extension, type) \ static type* drflac__full_decode_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)\ -{ \ - drflac_assert(pFlac != NULL); \ - \ - type* pSampleData = NULL; \ - drflac_uint64 totalSampleCount = pFlac->totalSampleCount; \ - \ - if (totalSampleCount == 0) { \ - type buffer[4096]; \ - \ - size_t sampleDataBufferSize = sizeof(buffer); \ - pSampleData = (type*)DRFLAC_MALLOC(sampleDataBufferSize); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - drflac_uint64 samplesRead; \ +{ \ + drflac_uint64 totalSampleCount; \ + type* pSampleData = NULL; \ + drflac_assert(pFlac != NULL); \ + totalSampleCount = pFlac->totalSampleCount; \ + if (totalSampleCount == 0) { \ + type buffer[4096]; \ + drflac_uint64 samplesRead; \ + size_t sampleDataBufferSize = sizeof(buffer); \ + pSampleData = (type*)DRFLAC_MALLOC(sampleDataBufferSize); \ + if (pSampleData == NULL) \ + goto on_error; \ while ((samplesRead = (drflac_uint64)drflac_read_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) { \ if (((totalSampleCount + samplesRead) * sizeof(type)) > sampleDataBufferSize) { \ + type *pNewSampleData; \ sampleDataBufferSize *= 2; \ - type* pNewSampleData = (type*)DRFLAC_REALLOC(pSampleData, sampleDataBufferSize); \ + pNewSampleData = (type*)DRFLAC_REALLOC(pSampleData, sampleDataBufferSize); \ if (pNewSampleData == NULL) { \ DRFLAC_FREE(pSampleData); \ goto on_error; \ From 64f31f1dddf978c35074c6cab2fcfdd9ce68cc40 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 13:52:54 +0200 Subject: [PATCH 507/517] Update libretro-common --- libretro-common/audio/audio_mixer.c | 193 +++++++++++++++++++- libretro-common/include/audio/audio_mixer.h | 5 +- 2 files changed, 194 insertions(+), 4 deletions(-) diff --git a/libretro-common/audio/audio_mixer.c b/libretro-common/audio/audio_mixer.c index b5f83f2e5c..d7a99a4dec 100644 --- a/libretro-common/audio/audio_mixer.c +++ b/libretro-common/audio/audio_mixer.c @@ -52,6 +52,12 @@ #include #endif + +#ifdef HAVE_DR_MP3 +#define DR_MP3_IMPLEMENTATION +#include +#endif + #ifdef HAVE_IBXM #include #endif @@ -90,6 +96,15 @@ struct audio_mixer_sound } flac; #endif +#ifdef HAVE_DR_MP3 + struct + { + /* mp */ + unsigned size; + const void* data; + } mp3; +#endif + #ifdef HAVE_IBXM struct { @@ -144,6 +159,20 @@ struct audio_mixer_voice } flac; #endif +#ifdef HAVE_DR_MP3 + struct + { + unsigned position; + unsigned samples; + unsigned buf_samples; + float* buffer; + float ratio; + drmp3 stream; + void *resampler_data; + const retro_resampler_t *resampler; + } mp3; +#endif + #ifdef HAVE_IBXM struct { @@ -390,6 +419,24 @@ audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size) #endif } +audio_mixer_sound_t* audio_mixer_load_mp3(void *buffer, int32_t size) +{ +#ifdef HAVE_DR_MP3 + audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound)); + + if (!sound) + return NULL; + + sound->type = AUDIO_MIXER_TYPE_MP3; + sound->types.mp3.size = size; + sound->types.mp3.data = buffer; + + return sound; +#else + return NULL; +#endif +} + audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size) { #ifdef HAVE_IBXM @@ -440,6 +487,13 @@ void audio_mixer_destroy(audio_mixer_sound_t* sound) handle = (void*)sound->types.flac.data; if (handle) free(handle); +#endif + break; + case AUDIO_MIXER_TYPE_MP3: +#ifdef HAVE_DR_MP3 + handle = (void*)sound->types.mp3.data; + if (handle) + free(handle); #endif break; case AUDIO_MIXER_TYPE_NONE: @@ -585,6 +639,7 @@ error: } #endif + #ifdef HAVE_DR_FLAC static bool audio_mixer_play_flac( audio_mixer_sound_t* sound, @@ -592,7 +647,6 @@ static bool audio_mixer_play_flac( bool repeat, float volume, audio_mixer_stop_cb_t stop_cb) { - int res = 0; float ratio = 1.0f; unsigned samples = 0; void *flac_buffer = NULL; @@ -640,6 +694,58 @@ error: } #endif +#ifdef HAVE_DR_MP3 +static bool audio_mixer_play_mp3( + audio_mixer_sound_t* sound, + audio_mixer_voice_t* voice, + bool repeat, float volume, + audio_mixer_stop_cb_t stop_cb) +{ + float ratio = 1.0f; + unsigned samples = 0; + void *mp3_buffer = NULL; + void *resampler_data = NULL; + const retro_resampler_t* resamp = NULL; + bool res =drmp3_init_memory(&voice->types.mp3.stream,(const unsigned char*)sound->types.mp3.data,sound->types.mp3.size,NULL); + if (!res) + return false; + if (voice->types.mp3.stream.sampleRate != s_rate) + { + ratio = (double)s_rate / (double)(voice->types.mp3.stream.sampleRate); + + if (!retro_resampler_realloc(&resampler_data, + &resamp, NULL, RESAMPLER_QUALITY_DONTCARE, + ratio)) + goto error; + } + + samples = (unsigned)(AUDIO_MIXER_TEMP_BUFFER * ratio); + mp3_buffer = (float*)memalign_alloc(16, + ((samples + 15) & ~15) * sizeof(float)); + + if (!mp3_buffer) + { + resamp->free(resampler_data); + goto error; + } + + voice->types.mp3.resampler = resamp; + voice->types.mp3.resampler_data = resampler_data; + voice->types.mp3.buffer = (float*)mp3_buffer; + voice->types.mp3.buf_samples = samples; + voice->types.mp3.ratio = ratio; + voice->types.mp3.position = 0; + voice->types.mp3.samples = 0; + + return true; + +error: + drmp3_uninit(&voice->types.mp3.stream); + return false; +} +#endif + + audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound, bool repeat, float volume, audio_mixer_stop_cb_t stop_cb) { @@ -677,6 +783,11 @@ audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound, bool repeat, case AUDIO_MIXER_TYPE_FLAC: #ifdef HAVE_DR_FLAC res = audio_mixer_play_flac(sound, voice, repeat, volume, stop_cb); +#endif + break; + case AUDIO_MIXER_TYPE_MP3: +#ifdef HAVE_DR_MP3 + res = audio_mixer_play_mp3(sound, voice, repeat, volume, stop_cb); #endif break; case AUDIO_MIXER_TYPE_NONE: @@ -966,7 +1077,7 @@ again: info.ratio = voice->types.flac.ratio; if (voice->types.flac.resampler) - voice->types.ogg.resampler->process(voice->types.flac.resampler_data, &info); + voice->types.flac.resampler->process(voice->types.flac.resampler_data, &info); else memcpy(voice->types.flac.buffer, temp_buffer, temp_samples * sizeof(float)); voice->types.flac.position = 0; @@ -995,6 +1106,79 @@ again: } #endif +#ifdef HAVE_DR_MP3 +static void audio_mixer_mix_mp3(float* buffer, size_t num_frames, + audio_mixer_voice_t* voice, + float volume) +{ + int i; + struct resampler_data info = { 0 }; + float temp_buffer[AUDIO_MIXER_TEMP_BUFFER] = { 0 }; + unsigned buf_free = (unsigned)(num_frames * 2); + unsigned temp_samples = 0; + float* pcm = NULL; + + if (voice->types.mp3.position == voice->types.mp3.samples) + { +again: + temp_samples = drmp3_read_f32(&voice->types.mp3.stream, AUDIO_MIXER_TEMP_BUFFER/2, temp_buffer) * 2; + + if (temp_samples == 0) + { + if (voice->repeat) + { + if (voice->stop_cb) + voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED); + + drmp3_seek_to_frame(&voice->types.mp3.stream,0); + goto again; + } + else + { + if (voice->stop_cb) + voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED); + + voice->type = AUDIO_MIXER_TYPE_NONE; + return; + } + } + + info.data_in = temp_buffer; + info.data_out = voice->types.mp3.buffer; + info.input_frames = temp_samples / 2; + info.output_frames = 0; + info.ratio = voice->types.mp3.ratio; + + if (voice->types.mp3.resampler) + voice->types.mp3.resampler->process(voice->types.mp3.resampler_data, &info); + else + memcpy(voice->types.mp3.buffer, temp_buffer, temp_samples * sizeof(float)); + voice->types.mp3.position = 0; + voice->types.mp3.samples = voice->types.mp3.buf_samples; + } + + pcm = voice->types.mp3.buffer + voice->types.mp3.position; + + if (voice->types.mp3.samples < buf_free) + { + for (i = voice->types.mp3.samples; i != 0; i--) + *buffer++ += *pcm++ * volume; + + buf_free -= voice->types.mp3.samples; + goto again; + } + else + { + int i; + for (i = buf_free; i != 0; --i ) + *buffer++ += *pcm++ * volume; + + voice->types.mp3.position += buf_free; + voice->types.mp3.samples -= buf_free; + } +} +#endif + void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bool override) { unsigned i; @@ -1028,6 +1212,11 @@ void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bo case AUDIO_MIXER_TYPE_FLAC: #ifdef HAVE_DR_FLAC audio_mixer_mix_flac(buffer, num_frames, voice, volume); +#endif + break; + case AUDIO_MIXER_TYPE_MP3: +#ifdef HAVE_DR_MP3 + audio_mixer_mix_mp3(buffer, num_frames, voice, volume); #endif break; case AUDIO_MIXER_TYPE_NONE: diff --git a/libretro-common/include/audio/audio_mixer.h b/libretro-common/include/audio/audio_mixer.h index 1541c431b7..a9c588372e 100644 --- a/libretro-common/include/audio/audio_mixer.h +++ b/libretro-common/include/audio/audio_mixer.h @@ -42,7 +42,8 @@ enum audio_mixer_type AUDIO_MIXER_TYPE_WAV, AUDIO_MIXER_TYPE_OGG, AUDIO_MIXER_TYPE_MOD, - AUDIO_MIXER_TYPE_FLAC + AUDIO_MIXER_TYPE_FLAC, + AUDIO_MIXER_TYPE_MP3 }; typedef struct audio_mixer_sound audio_mixer_sound_t; @@ -61,9 +62,9 @@ void audio_mixer_done(void); audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size); audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size); -audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size); audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size); audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size); +audio_mixer_sound_t* audio_mixer_load_mp3(void *buffer, int32_t size); void audio_mixer_destroy(audio_mixer_sound_t* sound); From e7272ddefacc0a3f9a160e3d5cb5777a6aba41dc Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 14:18:04 +0200 Subject: [PATCH 508/517] (Audio mixer) Add MP3 support as well --- Makefile.common | 2 ++ audio/audio_driver.c | 8 ++++++++ file_path_special.h | 1 + file_path_str.c | 3 +++ tasks/task_audio_mixer.c | 28 ++++++++++++++++++++++++++++ tasks/task_file_transfer.c | 1 + tasks/tasks_internal.h | 1 + 7 files changed, 44 insertions(+) diff --git a/Makefile.common b/Makefile.common index 8715b2d5ae..d17d8a3562 100644 --- a/Makefile.common +++ b/Makefile.common @@ -147,6 +147,8 @@ ifneq ($(GIT_VERSION),) endif # General object files +DEFINES += -DHAVE_DR_MP3 +CFLAGS += -DHAVE_DR_MP3 OBJ += frontend/frontend.o \ frontend/frontend_driver.o \ diff --git a/audio/audio_driver.c b/audio/audio_driver.c index bd810910a6..7db29ef441 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -979,6 +979,9 @@ bool audio_driver_mixer_extension_supported(const char *ext) #endif #ifdef HAVE_DR_FLAC string_list_append(str_list, "flac", attr); +#endif +#ifdef HAVE_DR_MP3 + string_list_append(str_list, "mp3", attr); #endif string_list_append(str_list, "wav", attr); @@ -1080,6 +1083,11 @@ bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params) case AUDIO_MIXER_TYPE_FLAC: #ifdef HAVE_DR_FLAC handle = audio_mixer_load_flac(buf, (int32_t)params->bufsize); +#endif + break; + case AUDIO_MIXER_TYPE_MP3: +#ifdef HAVE_DR_MP3 + handle = audio_mixer_load_mp3(buf, (int32_t)params->bufsize); #endif break; case AUDIO_MIXER_TYPE_NONE: diff --git a/file_path_special.h b/file_path_special.h index a1b138c7d2..16455f2486 100644 --- a/file_path_special.h +++ b/file_path_special.h @@ -86,6 +86,7 @@ enum file_path_enum FILE_PATH_ZIP_EXTENSION, FILE_PATH_7Z_EXTENSION, FILE_PATH_OGG_EXTENSION, + FILE_PATH_MP3_EXTENSION, FILE_PATH_FLAC_EXTENSION, FILE_PATH_WAV_EXTENSION, FILE_PATH_MOD_EXTENSION, diff --git a/file_path_str.c b/file_path_str.c index d741109c56..2d9d73481d 100644 --- a/file_path_str.c +++ b/file_path_str.c @@ -101,6 +101,9 @@ const char *file_path_str(enum file_path_enum enum_idx) case FILE_PATH_PNG_EXTENSION: str = ".png"; break; + case FILE_PATH_MP3_EXTENSION: + str = ".mp3"; + break; case FILE_PATH_FLAC_EXTENSION: str = ".flac"; break; diff --git a/tasks/task_audio_mixer.c b/tasks/task_audio_mixer.c index 0331d8dbcf..69772849fe 100644 --- a/tasks/task_audio_mixer.c +++ b/tasks/task_audio_mixer.c @@ -124,6 +124,28 @@ static void task_audio_mixer_handle_upload_flac(void *task_data, free(user_data); } +static void task_audio_mixer_handle_upload_mp3(void *task_data, + void *user_data, const char *err) +{ + audio_mixer_stream_params_t params; + nbio_buf_t *img = (nbio_buf_t*)task_data; + + if (!img) + return; + + params.volume = 1.0f; + params.type = AUDIO_MIXER_TYPE_MP3; + params.state = AUDIO_STREAM_STATE_PLAYING; + params.buf = img->buf; + params.bufsize = img->bufsize; + params.cb = NULL; + + audio_driver_mixer_add_stream(¶ms); + + free(img); + free(user_data); +} + static void task_audio_mixer_handle_upload_mod(void *task_data, void *user_data, const char *err) { @@ -238,6 +260,12 @@ bool task_push_audio_mixer_load(const char *fullpath, retro_task_callback_t cb, nbio->type = NBIO_TYPE_OGG; t->callback = task_audio_mixer_handle_upload_ogg; } + else if (strstr(fullpath, file_path_str(FILE_PATH_MP3_EXTENSION))) + { + image->type = AUDIO_MIXER_TYPE_MP3; + nbio->type = NBIO_TYPE_MP3; + t->callback = task_audio_mixer_handle_upload_mp3; + } else if (strstr(fullpath, file_path_str(FILE_PATH_FLAC_EXTENSION))) { image->type = AUDIO_MIXER_TYPE_FLAC; diff --git a/tasks/task_file_transfer.c b/tasks/task_file_transfer.c index 849c712267..2249681508 100644 --- a/tasks/task_file_transfer.c +++ b/tasks/task_file_transfer.c @@ -103,6 +103,7 @@ void task_file_load_handler(retro_task_t *task) if (!task_image_load_handler(task)) task_set_finished(task, true); break; + case NBIO_TYPE_MP3: case NBIO_TYPE_FLAC: case NBIO_TYPE_OGG: case NBIO_TYPE_MOD: diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index 426e3fbf31..766f593d14 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -74,6 +74,7 @@ enum nbio_type NBIO_TYPE_BMP, NBIO_TYPE_OGG, NBIO_TYPE_FLAC, + NBIO_TYPE_MP3, NBIO_TYPE_MOD, NBIO_TYPE_WAV }; From 3583e1e501f6fb73dfab33848adb600465aeb5b2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 14:22:42 +0200 Subject: [PATCH 509/517] Update --- deps/dr/dr_mp3.h | 2712 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2712 insertions(+) create mode 100644 deps/dr/dr_mp3.h diff --git a/deps/dr/dr_mp3.h b/deps/dr/dr_mp3.h new file mode 100644 index 0000000000..01483843f9 --- /dev/null +++ b/deps/dr/dr_mp3.h @@ -0,0 +1,2712 @@ +#ifndef dr_mp3_h +#define dr_mp3_h + +#define DR_MP3_NO_STDIO + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef int8_t drmp3_int8; +typedef uint8_t drmp3_uint8; +typedef int16_t drmp3_int16; +typedef uint16_t drmp3_uint16; +typedef int32_t drmp3_int32; +typedef uint32_t drmp3_uint32; +typedef int64_t drmp3_int64; +typedef uint64_t drmp3_uint64; +typedef drmp3_uint8 drmp3_bool8; +typedef drmp3_uint32 drmp3_bool32; +#define DRMP3_TRUE 1 +#define DRMP3_FALSE 0 + +#define DRMP3_MAX_SAMPLES_PER_FRAME (1152*2) + + +/* Low Level Push API + * ================== */ +typedef struct +{ + int frame_bytes; + int channels; + int hz; + int layer; + int bitrate_kbps; +} drmp3dec_frame_info; + +typedef struct +{ + float mdct_overlap[2][9*32]; + float qmf_state[15*2*32]; + int reserv; + int free_format_bytes; + unsigned char header[4]; + unsigned char reserv_buf[511]; +} drmp3dec; + +/* Initializes a low level decoder. */ +void drmp3dec_init(drmp3dec *dec); + +/* Reads a frame from a low level decoder. */ +int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info); + + +/* Main API (Pull API) + * ===================*/ + +typedef struct drmp3_src drmp3_src; +typedef drmp3_uint64 (* drmp3_src_read_proc)(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData); /* Returns the number of frames that were read. */ + +typedef enum +{ + drmp3_src_algorithm_none, + drmp3_src_algorithm_linear +} drmp3_src_algorithm; + +#define DRMP3_SRC_CACHE_SIZE_IN_FRAMES 512 +typedef struct +{ + drmp3_src* pSRC; + float pCachedFrames[2 * DRMP3_SRC_CACHE_SIZE_IN_FRAMES]; + drmp3_uint32 cachedFrameCount; + drmp3_uint32 iNextFrame; +} drmp3_src_cache; + +typedef struct +{ + drmp3_uint32 sampleRateIn; + drmp3_uint32 sampleRateOut; + drmp3_uint32 channels; + drmp3_src_algorithm algorithm; + drmp3_uint32 cacheSizeInFrames; /* The number of frames to read from the client at a time. */ +} drmp3_src_config; + +struct drmp3_src +{ + drmp3_src_config config; + drmp3_src_read_proc onRead; + void* pUserData; + float bin[256]; + drmp3_src_cache cache; /* <-- For simplifying and optimizing client -> memory reading. */ + union + { + struct + { + float alpha; + drmp3_bool32 isPrevFramesLoaded : 1; + drmp3_bool32 isNextFramesLoaded : 1; + } linear; + } algo; +}; + +typedef enum +{ + drmp3_seek_origin_start, + drmp3_seek_origin_current +} drmp3_seek_origin; + +/* Callback for when data is read. Return value is the number of bytes actually read. + * + * pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. + * pBufferOut [out] The output buffer. + * bytesToRead [in] The number of bytes to read. + * + * Returns the number of bytes actually read. + * + * A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until + * either the entire bytesToRead is filled or you have reached the end of the stream. + */ +typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +/* Callback for when data needs to be seeked. + * + * pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. + * offset [in] The number of bytes to move, relative to the origin. Will never be negative. + * origin [in] The origin of the seek - the current position or the start of the stream. + * + * Returns whether or not the seek was successful. + * + * Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which + * will be either drmp3_seek_origin_start or drmp3_seek_origin_current. + */ +typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); + +typedef struct +{ + drmp3_uint32 outputChannels; + drmp3_uint32 outputSampleRate; +} drmp3_config; + +typedef struct +{ + drmp3dec decoder; + drmp3dec_frame_info frameInfo; + drmp3_uint32 channels; + drmp3_uint32 sampleRate; + drmp3_read_proc onRead; + drmp3_seek_proc onSeek; + void* pUserData; + drmp3_uint32 frameChannels; /* The number of channels in the currently loaded MP3 frame. Internal use only. */ + drmp3_uint32 frameSampleRate; /* The sample rate of the currently loaded MP3 frame. Internal use only */ + drmp3_uint32 framesConsumed; + drmp3_uint32 framesRemaining; + drmp3_int16 frames[DRMP3_MAX_SAMPLES_PER_FRAME]; + drmp3_src src; + size_t dataSize; + size_t dataCapacity; + drmp3_uint8* pData; + drmp3_bool32 atEnd : 1; + struct + { + const drmp3_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; /* Only used for decoders that were opened against a block of memory. */ +} drmp3; + +/* Initializes an MP3 decoder. + * + * onRead [in] The function to call when data needs to be read from the client. + * onSeek [in] The function to call when the read position of the client data needs to move. + * pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. + * + * Returns true if successful; false otherwise. + * + * Close the loader with drmp3_uninit(). + * + * See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit() + */ +drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig); + +/* Initializes an MP3 decoder from a block of memory. + * + * This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for + * the lifetime of the drmp3 object. + * + * The buffer should contain the contents of the entire MP3 file. + */ +drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_config* pConfig); + +#ifndef DR_MP3_NO_STDIO +/* Initializes an MP3 decoder from a file. + * + * This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3 + * objects because the operating system may restrict the number of file handles an application can have open at + * any given time. + */ +drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig); +#endif + +/* Uninitializes an MP3 decoder. */ +void drmp3_uninit(drmp3* pMP3); + +/* Reads PCM frames as interleaved 32-bit IEEE floating point PCM. + * + * Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames. + */ +drmp3_uint64 drmp3_read_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); + +/* Seeks to a specific frame. + * + * Note that this is _not_ an MP3 frame, but rather a PCM frame. + */ +drmp3_bool32 drmp3_seek_to_frame(drmp3* pMP3, drmp3_uint64 frameIndex); + + +/* Opens an decodes an entire MP3 stream as a single operation. + * + * pConfig is both an input and output. On input it contains what you want. On output it contains what you got. + * + * Free the returned pointer with drmp3_free(). + */ +float* drmp3_open_and_decode_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount); +float* drmp3_open_and_decode_memory_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount); +#ifndef DR_MP3_NO_STDIO +float* drmp3_open_and_decode_file_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount); +#endif + +/* Frees any memory that was allocated by a public drmp3 API. */ +void drmp3_free(void* p); + +#ifdef __cplusplus +} +#endif +#endif /* dr_mp3_h */ + + +/* + * + * IMPLEMENTATION + * + */ +#ifdef DR_MP3_IMPLEMENTATION +#include +#include +#include +#include /* For INT_MAX */ + +#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#define DRMP3_MAX_FRAME_SYNC_MATCHES 10 + +#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ + +#define DRMP3_MAX_BITRESERVOIR_BYTES 511 +#define DRMP3_SHORT_BLOCK_TYPE 2 +#define DRMP3_STOP_BLOCK_TYPE 3 +#define DRMP3_MODE_MONO 3 +#define DRMP3_MODE_JOINT_STEREO 1 +#define DRMP3_HDR_SIZE 4 +#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) +#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) + +#define DRMP3_BITS_DEQUANTIZER_OUT -1 +#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) +#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) + +#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) + +#if !defined(DR_MP3_NO_SIMD) + +#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(_M_ARM64) || defined(__x86_64__) || defined(__aarch64__)) +/* x64 always have SSE2, arm64 always have neon, no need for generic code */ +#define DR_MP3_ONLY_SIMD +#endif + +#if (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__)) +#if defined(_MSC_VER) +#include +#endif +#include +#define DRMP3_HAVE_SSE 1 +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE _mm_storeu_ps +#define DRMP3_VLD _mm_loadu_ps +#define DRMP3_VSET _mm_set1_ps +#define DRMP3_VADD _mm_add_ps +#define DRMP3_VSUB _mm_sub_ps +#define DRMP3_VMUL _mm_mul_ps +#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 drmp3_f4; +#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) +#define drmp3_cpuid __cpuid +#else +static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) +{ +#if defined(__PIC__) + __asm__ __volatile__( +#if defined(__x86_64__) + "push %%rbx\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + "pop %%rbx\n" +#else + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" +#endif + : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#else + __asm__ __volatile__( + "cpuid" + : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#endif +} +#endif +static int drmp3_have_simd() +{ +#ifdef DR_MP3_ONLY_SIMD + return 1; +#else + static int g_have_simd; + int CPUInfo[4]; +#ifdef MINIMP3_TEST + static int g_counter; + if (g_counter++ > 100) + goto test_nosimd; +#endif + if (g_have_simd) + return g_have_simd - 1; + drmp3_cpuid(CPUInfo, 0); + if (CPUInfo[0] > 0) + { + drmp3_cpuid(CPUInfo, 1); + g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */ + return g_have_simd - 1; + } +#ifdef MINIMP3_TEST +test_nosimd: +#endif + g_have_simd = 1; + return 0; +#endif +} +#elif defined(__ARM_NEON) || defined(__aarch64__) +#include +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE vst1q_f32 +#define DRMP3_VLD vld1q_f32 +#define DRMP3_VSET vmovq_n_f32 +#define DRMP3_VADD vaddq_f32 +#define DRMP3_VSUB vsubq_f32 +#define DRMP3_VMUL vmulq_f32 +#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) +#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) +#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t drmp3_f4; +static int drmp3_have_simd() +{ /* TODO: detect neon for !DR_MP3_ONLY_SIMD */ + return 1; +} +#else +#define DRMP3_HAVE_SIMD 0 +#ifdef DR_MP3_ONLY_SIMD +#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled +#endif +#endif + +#else + +#define DRMP3_HAVE_SIMD 0 + +#endif + +typedef struct +{ + const drmp3_uint8 *buf; + int pos; + int limit; +} drmp3_bs; + +typedef struct +{ + drmp3_uint8 total_bands; + drmp3_uint8 stereo_bands; + drmp3_uint8 bitalloc[64]; + drmp3_uint8 scfcod[64]; + float scf[3*64]; +} drmp3_L12_scale_info; + +typedef struct +{ + drmp3_uint8 tab_offset; + drmp3_uint8 code_tab_width; + drmp3_uint8 band_count; +} drmp3_L12_subband_alloc; + +typedef struct +{ + const drmp3_uint8 *sfbtab; + drmp3_uint16 part_23_length; + drmp3_uint16 big_values; + drmp3_uint16 scalefac_compress; + drmp3_uint8 global_gain; + drmp3_uint8 block_type; + drmp3_uint8 mixed_block_flag; + drmp3_uint8 n_long_sfb; + drmp3_uint8 n_short_sfb; + drmp3_uint8 table_select[3]; + drmp3_uint8 region_count[3]; + drmp3_uint8 subblock_gain[3]; + drmp3_uint8 preflag; + drmp3_uint8 scalefac_scale; + drmp3_uint8 count1_table; + drmp3_uint8 scfsi; +} drmp3_L3_gr_info; + +typedef struct +{ + drmp3_bs bs; + drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + drmp3_L3_gr_info gr_info[4]; + float grbuf[2][576]; + float scf[40]; + drmp3_uint8 ist_pos[2][39]; + float syn[18 + 15][2*32]; +} drmp3dec_scratch; + +static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) +{ + bs->buf = data; + bs->pos = 0; + bs->limit = bytes*8; +} + +static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) +{ + drmp3_uint32 next, cache = 0, s = bs->pos & 7; + int shl = n + s; + const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); + if ((bs->pos += n) > bs->limit) + return 0; + next = *p++ & (255 >> s); + while ((shl -= 8) > 0) + { + cache |= next << shl; + next = *p++; + } + return cache | (next >> -shl); +} + +static int drmp3_hdr_valid(const drmp3_uint8 *h) +{ + return h[0] == 0xff && + ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + (DRMP3_HDR_GET_LAYER(h) != 0) && + (DRMP3_HDR_GET_BITRATE(h) != 15) && + (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); +} + +static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) +{ + return drmp3_hdr_valid(h2) && + ((h1[1] ^ h2[1]) & 0xFE) == 0 && + ((h1[2] ^ h2[2]) & 0x0C) == 0 && + !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); +} + +static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) +{ + static const drmp3_uint8 halfrate[2][3][15] = { + { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, + { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, + }; + return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; +} + +static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) +{ + static const unsigned g_hz[3] = { 44100, 48000, 32000 }; + return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); +} + +static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) +{ + return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); +} + +static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) +{ + int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); + if (DRMP3_HDR_IS_LAYER_1(h)) + { + frame_bytes &= ~3; /* slot align */ + } + return frame_bytes ? frame_bytes : free_format_size; +} + +static int drmp3_hdr_padding(const drmp3_uint8 *h) +{ + return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; +} + +#ifndef DR_MP3_ONLY_MP3 +static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) +{ + const drmp3_L12_subband_alloc *alloc; + int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + + if (DRMP3_HDR_IS_LAYER_1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; + alloc = g_alloc_L1; + nbands = 32; + } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + alloc = g_alloc_L2M2; + nbands = 30; + } else + { + static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); + if (!kbps) /* free-format */ + { + kbps = 192; + } + + alloc = g_alloc_L2M1; + nbands = 27; + if (kbps < 56) + { + static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + alloc = g_alloc_L2M1_lowrate; + nbands = sample_rate_idx == 2 ? 12 : 8; + } else if (kbps >= 96 && sample_rate_idx != 1) + { + nbands = 30; + } + } + + sci->total_bands = (drmp3_uint8)nbands; + sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); + + return alloc; +} + +static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) +{ + static const float g_deq_L12[18*3] = { +#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) + }; + int i, m; + for (i = 0; i < bands; i++) + { + float s = 0; + int ba = *pba++; + int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; + for (m = 4; m; m >>= 1) + { + if (mask & m) + { + int b = drmp3_bs_get_bits(bs, 6); + s = g_deq_L12[ba*3 - 6 + b % 3]*(1 << 21 >> b/3); + } + *scf++ = s; + } + } +} + +static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) +{ + static const drmp3_uint8 g_bitalloc_code_tab[] = { + 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, + 0,17,18, 3,19,4,5,16, + 0,17,18,16, + 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, + 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 + }; + const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); + + int i, k = 0, ba_bits = 0; + const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; + + for (i = 0; i < sci->total_bands; i++) + { + drmp3_uint8 ba; + if (i == k) + { + k += subband_alloc->band_count; + ba_bits = subband_alloc->code_tab_width; + ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; + subband_alloc++; + } + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + sci->bitalloc[2*i] = ba; + if (i < sci->stereo_bands) + { + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + } + sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; + } + + for (i = 0; i < 2*sci->total_bands; i++) + { + sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); + } + + drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + + for (i = sci->stereo_bands; i < sci->total_bands; i++) + { + sci->bitalloc[2*i + 1] = 0; + } +} + +static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) +{ + int i, j, k, choff = 576; + for (j = 0; j < 4; j++) + { + float *dst = grbuf + group_size*j; + for (i = 0; i < 2*sci->total_bands; i++) + { + int ba = sci->bitalloc[i]; + if (ba != 0) + { + if (ba < 17) + { + int half = (1 << (ba - 1)) - 1; + for (k = 0; k < group_size; k++) + { + dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); + } + } else + { + unsigned mod = (2 << (ba - 17)) + 1; /* 3, 5, 9 */ + unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); /* 5, 7, 10 */ + for (k = 0; k < group_size; k++, code /= mod) + { + dst[k] = (float)((int)(code % mod - mod/2)); + } + } + } + dst += choff; + choff = 18 - choff; + } + } + return group_size*4; +} + +static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) +{ + int i, k; + memcpy(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) + { + for (k = 0; k < 12; k++) + { + dst[k + 0] *= scf[0]; + dst[k + 576] *= scf[3]; + } + } +} +#endif + +static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +{ + static const drmp3_uint8 g_scf_long[9][23] = { + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, + { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, + { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } + }; + static const drmp3_uint8 g_scf_short[9][40] = { + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + static const drmp3_uint8 g_scf_mixed[9][40] = { + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + + unsigned tables, scfsi = 0; + int main_data_begin, part_23_sum = 0; + int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); + int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + gr_count *= 2; + main_data_begin = drmp3_bs_get_bits(bs, 9); + scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); + } else + { + main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + } + + do + { + if (DRMP3_HDR_IS_MONO(hdr)) + { + scfsi <<= 4; + } + gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); + part_23_sum += gr->part_23_length; + gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); + if (gr->big_values > 288) + { + return -1; + } + gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); + gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->sfbtab = g_scf_long[sr_idx]; + gr->n_long_sfb = 22; + gr->n_short_sfb = 0; + if (drmp3_bs_get_bits(bs, 1)) + { + gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); + if (!gr->block_type) + { + return -1; + } + gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->region_count[0] = 7; + gr->region_count[1] = 255; + if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) + { + scfsi &= 0x0F0F; + if (!gr->mixed_block_flag) + { + gr->region_count[0] = 8; + gr->sfbtab = g_scf_short[sr_idx]; + gr->n_long_sfb = 0; + gr->n_short_sfb = 39; + } else + { + gr->sfbtab = g_scf_mixed[sr_idx]; + gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_short_sfb = 30; + } + } + tables = drmp3_bs_get_bits(bs, 10); + tables <<= 5; + gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + } else + { + gr->block_type = 0; + gr->mixed_block_flag = 0; + tables = drmp3_bs_get_bits(bs, 15); + gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); + gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->region_count[2] = 255; + } + gr->table_select[0] = (drmp3_uint8)(tables >> 10); + gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); + gr->table_select[2] = (drmp3_uint8)((tables) & 31); + gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); + scfsi <<= 4; + gr++; + } while(--gr_count); + + if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) + { + return -1; + } + + return main_data_begin; +} + +static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) +{ + int i, k; + for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) + { + int cnt = scf_count[i]; + if (scfsi & 8) + { + memcpy(scf, ist_pos, cnt); + } else + { + int bits = scf_size[i]; + if (!bits) + { + memset(scf, 0, cnt); + memset(ist_pos, 0, cnt); + } else + { + int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; + for (k = 0; k < cnt; k++) + { + int s = drmp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); + scf[k] = (drmp3_uint8)s; + } + } + } + ist_pos += cnt; + scf += cnt; + } + scf[0] = scf[1] = scf[2] = 0; +} + +static float drmp3_L3_ldexp_q2(float y, int exp_q2) +{ + static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; + int e; + do + { + e = DRMP3_MIN(30*4, exp_q2); + y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); + } while ((exp_q2 -= e) > 0); + return y; +} + +static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) +{ + static const drmp3_uint8 g_scf_partitions[3][28] = { + { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, + { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, + { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } + }; + const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + drmp3_uint8 scf_size[4], iscf[40]; + int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; + float gain; + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + int part = g_scfc_decode[gr->scalefac_compress]; + scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); + } else + { + static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; + sfc = gr->scalefac_compress >> ist; + for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) + { + for (modprod = 1, i = 3; i >= 0; i--) + { + scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); + modprod *= g_mod[k + i]; + } + } + scf_partition += k; + scfsi = -16; + } + drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + + if (gr->n_short_sfb) + { + int sh = 3 - scf_shift; + for (i = 0; i < gr->n_short_sfb; i += 3) + { + iscf[gr->n_long_sfb + i + 0] += gr->subblock_gain[0] << sh; + iscf[gr->n_long_sfb + i + 1] += gr->subblock_gain[1] << sh; + iscf[gr->n_long_sfb + i + 2] += gr->subblock_gain[2] << sh; + } + } else if (gr->preflag) + { + static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + for (i = 0; i < 10; i++) + { + iscf[11 + i] += g_preamp[i]; + } + } + + gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); + for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) + { + scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + } +} + +static float drmp3_L3_pow_43(int x) +{ + static const float g_pow43[129] = { + 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f + }; + float frac; + int sign, mult = 256; + + if (x < 129) + { + return g_pow43[x]; + } + + if (x < 1024) + { + mult = 16; + x <<= 3; + } + + sign = 2*x & 64; + frac = (float)((x & 63) - sign) / ((x & ~63) + sign); + return g_pow43[(x + sign) >> 6]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; +} + +static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) +{ + static const float g_pow43_signed[32] = { 0,0,1,-1,2.519842f,-2.519842f,4.326749f,-4.326749f,6.349604f,-6.349604f,8.549880f,-8.549880f,10.902724f,-10.902724f,13.390518f,-13.390518f,16.000000f,-16.000000f,18.720754f,-18.720754f,21.544347f,-21.544347f,24.463781f,-24.463781f,27.473142f,-27.473142f,30.567351f,-30.567351f,33.741992f,-33.741992f,36.993181f,-36.993181f }; + static const drmp3_int16 tab0[32] = { 0, }; + static const drmp3_int16 tab1[] = { 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256 }; + static const drmp3_int16 tab2[] = { -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288 }; + static const drmp3_int16 tab3[] = { -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288 }; + static const drmp3_int16 tab5[] = { -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258 }; + static const drmp3_int16 tab6[] = { -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259 }; + static const drmp3_int16 tab7[] = { -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258 }; + static const drmp3_int16 tab8[] = { -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258 }; + static const drmp3_int16 tab9[] = { -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259 }; + static const drmp3_int16 tab10[] = { -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258 }; + static const drmp3_int16 tab11[] = { -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290 }; + static const drmp3_int16 tab12[] = { -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259 }; + static const drmp3_int16 tab13[] = { -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258 }; + static const drmp3_int16 tab15[] = { -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259 }; + static const drmp3_int16 tab16[] = { -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258 }; + static const drmp3_int16 tab24[] = { -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; + static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; + static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const drmp3_int16 * const tabindex[2*16] = { tab0,tab1,tab2,tab3,tab0,tab5,tab6,tab7,tab8,tab9,tab10,tab11,tab12,tab13,tab0,tab15,tab16,tab16,tab16,tab16,tab16,tab16,tab16,tab16,tab24,tab24,tab24,tab24,tab24,tab24,tab24,tab24 }; + static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; + +#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n)) +#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + + float one = 0.0f; + int ireg = 0, big_val_cnt = gr_info->big_values; + const drmp3_uint8 *sfb = gr_info->sfbtab; + const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; + drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; + bs_next_ptr += 4; + + while (big_val_cnt > 0) + { + int tab_num = gr_info->table_select[ireg]; + int sfb_cnt = gr_info->region_count[ireg++]; + const short *codebook = tabindex[tab_num]; + int linbits = g_linbits[tab_num]; + do + { + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) + { + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + } + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15 && linbits) + { + lsb += DRMP3_PEEK_BITS(linbits); + DRMP3_FLUSH_BITS(linbits); + DRMP3_CHECK_BITS; + *dst = one*drmp3_L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1); + } else + { + *dst = g_pow43_signed[lsb*2 + (bs_cache >> 31)]*one; + } + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + + for (np = 1 - big_val_cnt;; dst += 4) + { + const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; + if (!(leaf & 8)) + { + leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; + } + DRMP3_FLUSH_BITS(leaf & 7); + if (DRMP3_BSPOS > layer3gr_limit) + { + break; + } +#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(0); + DRMP3_DEQ_COUNT1(1); + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(2); + DRMP3_DEQ_COUNT1(3); + DRMP3_CHECK_BITS; + } + + bs->pos = layer3gr_limit; +} + +static void drmp3_L3_midside_stereo(float *left, int n) +{ + int i = 0; + float *right = left + 576; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < n - 3; i += 4) + { + drmp3_f4 vl = DRMP3_VLD(left + i); + drmp3_f4 vr = DRMP3_VLD(right + i); + DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); + DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); + } +#endif + for (; i < n; i++) + { + float a = left[i]; + float b = right[i]; + left[i] = a + b; + right[i] = a - b; + } +} + +static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) +{ + int i; + for (i = 0; i < n; i++) + { + left[i + 576] = left[i]*kr; + left[i] = left[i]*kl; + } +} + +static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) +{ + int i, k; + + max_band[0] = max_band[1] = max_band[2] = -1; + + for (i = 0; i < nbands; i++) + { + for (k = 0; k < sfb[i]; k += 2) + { + if (right[k] != 0 || right[k + 1] != 0) + { + max_band[i % 3] = i; + break; + } + } + right += sfb[i]; + } +} + +static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) +{ + static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; + unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + + for (i = 0; sfb[i]; i++) + { + unsigned ipos = ist_pos[i]; + if ((int)i > max_band[i % 3] && ipos < max_pos) + { + float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + kl = g_pan[2*ipos]; + kr = g_pan[2*ipos + 1]; + } else + { + kl = 1; + kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + if (ipos & 1) + { + kl = kr; + kr = 1; + } + } + drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) + { + drmp3_L3_midside_stereo(left, sfb[i]); + } + left += sfb[i]; + } +} + +static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +{ + int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; + int i, max_blocks = gr->n_short_sfb ? 3 : 1; + + drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + if (gr->n_long_sfb) + { + max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); + } + for (i = 0; i < max_blocks; i++) + { + int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int itop = n_sfb - max_blocks + i; + int prev = itop - max_blocks; + ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + } + drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress&1); +} + +static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) +{ + int i, len; + float *src = grbuf, *dst = scratch; + + for (;0 != (len = *sfb); sfb += 3, src += 2*len) + { + for (i = 0; i < len; i++, src++) + { + *dst++ = src[0*len]; + *dst++ = src[1*len]; + *dst++ = src[2*len]; + } + } + memcpy(grbuf, scratch, (dst - scratch)*sizeof(float)); +} + +static void drmp3_L3_antialias(float *grbuf, int nbands) +{ + static const float g_aa[2][8] = { + {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, + {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} + }; + + for (; nbands > 0; nbands--, grbuf += 18) + { + int i = 0; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) + { + drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); + drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); + drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); + drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); + vd = DRMP3_VREV(vd); + DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); + vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); + } +#endif +#ifndef DR_MP3_ONLY_SIMD + for(; i < 8; i++) + { + float u = grbuf[18 + i]; + float d = grbuf[17 - i]; + grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; + grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; + } +#endif + } +} + +static void drmp3_L3_dct3_9(float *y) +{ + float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; + + s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; + t0 = s0 + s6*0.5f; + s0 -= s6; + t4 = (s4 + s2)*0.93969262f; + t2 = (s8 + s2)*0.76604444f; + s6 = (s4 - s8)*0.17364818f; + s4 += s8 - s2; + + s2 = s0 - s4*0.5f; + y[4] = s4 + s0; + s8 = t0 - t2 + s6; + s0 = t0 - t4 + t2; + s4 = t0 + t4 - s6; + + s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; + + s3 *= 0.86602540f; + t0 = (s5 + s1)*0.98480775f; + t4 = (s5 - s7)*0.34202014f; + t2 = (s1 + s7)*0.64278761f; + s1 = (s1 - s5 - s7)*0.86602540f; + + s5 = t0 - s3 - t2; + s7 = t4 - s3 - t0; + s3 = t4 + s3 - t2; + + y[0] = s4 - s7; + y[1] = s2 + s1; + y[2] = s0 - s3; + y[3] = s8 + s5; + y[5] = s8 - s5; + y[6] = s0 + s3; + y[7] = s2 - s1; + y[8] = s4 + s7; +} + +static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +{ + int i, j; + static const float g_twid9[18] = { + 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f + }; + + for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) + { + float co[9], si[9]; + co[0] = -grbuf[0]; + si[0] = grbuf[17]; + for (i = 0; i < 4; i++) + { + si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; + co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; + si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; + co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); + } + drmp3_L3_dct3_9(co); + drmp3_L3_dct3_9(si); + + si[1] = -si[1]; + si[3] = -si[3]; + si[5] = -si[5]; + si[7] = -si[7]; + + i = 0; + +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) + { + drmp3_f4 vovl = DRMP3_VLD(overlap + i); + drmp3_f4 vc = DRMP3_VLD(co + i); + drmp3_f4 vs = DRMP3_VLD(si + i); + drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); + drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); + drmp3_f4 vw0 = DRMP3_VLD(window + i); + drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); + drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); + DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); + DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); + vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); + } +#endif + for (; i < 9; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; + overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; + grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; + grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; + } + } +} + +static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) +{ + float m1 = x1*0.86602540f; + float a1 = x0 - x2*0.5f; + dst[1] = x0 + x2; + dst[0] = a1 + m1; + dst[2] = a1 - m1; +} + +static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) +{ + static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; + float co[3], si[3]; + int i; + + drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + si[1] = -si[1]; + + for (i = 0; i < 3; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; + overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; + dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; + dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; + } +} + +static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) +{ + for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) + { + float tmp[18]; + memcpy(tmp, grbuf, sizeof(tmp)); + memcpy(grbuf, overlap, 6*sizeof(float)); + drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + } +} + +static void drmp3_L3_change_sign(float *grbuf) +{ + int b, i; + for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) + for (i = 1; i < 18; i += 2) + grbuf[i] = -grbuf[i]; +} + +static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +{ + static const float g_mdct_window[2][18] = { + { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, + { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } + }; + if (n_long_bands) + { + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + grbuf += 18*n_long_bands; + overlap += 9*n_long_bands; + } + if (block_type == DRMP3_SHORT_BLOCK_TYPE) + drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + else + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); +} + +static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) +{ + int pos = (s->bs.pos + 7)/8u; + int remains = s->bs.limit/8u - pos; + if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) + { + pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; + remains = DRMP3_MAX_BITRESERVOIR_BYTES; + } + if (remains > 0) + { + memmove(h->reserv_buf, s->maindata + pos, remains); + } + h->reserv = remains; +} + +static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) +{ + int frame_bytes = (bs->limit - bs->pos)/8; + int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); + memcpy(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); + memcpy(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + return h->reserv >= main_data_begin; +} + +static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) +{ + int ch; + + for (ch = 0; ch < nch; ch++) + { + int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; + drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + } + + if (DRMP3_HDR_TEST_I_STEREO(h->header)) + { + drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) + { + drmp3_L3_midside_stereo(s->grbuf[0], 576); + } + + for (ch = 0; ch < nch; ch++, gr_info++) + { + int aa_bands = 31; + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + + if (gr_info->n_short_sfb) + { + aa_bands = n_long_bands - 1; + drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + } + + drmp3_L3_antialias(s->grbuf[ch], aa_bands); + drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + drmp3_L3_change_sign(s->grbuf[ch]); + } +} + +static void drmp3d_DCT_II(float *grbuf, int n) +{ + static const float g_sec[24] = { + 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f + }; + int i, k = 0; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; k < n; k += 4) + { + drmp3_f4 t[4][8], *x; + float *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); + drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); + drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); + drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); + drmp3_f4 t0 = DRMP3_VADD(x0, x3); + drmp3_f4 t1 = DRMP3_VADD(x1, x2); + drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); + drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = DRMP3_VADD(t0, t1); + x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = DRMP3_VADD(t3, t2); + x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); + x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); + x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); + x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); + x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); + x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); + x[0] = DRMP3_VADD(x0, x1); + x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); + x5 = DRMP3_VADD(x5, x6); + x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); + x7 = DRMP3_VADD(x7, xt); + x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); /* rotate by PI/8 */ + x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); + x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); + x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); + x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); + x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); + x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); + x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); + x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); + } + + if (k > n - 3) + { +#if DRMP3_HAVE_SSE +#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#else +#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v)) +#endif + for (i = 0; i < 7; i++, y += 4*18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE2(0, t[0][i]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE2(0, t[0][7]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE2(2, t[1][7]); + DRMP3_VSAVE2(3, t[3][7]); + } else + { +#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v) + for (i = 0; i < 7; i++, y += 4*18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE4(0, t[0][i]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE4(0, t[0][7]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE4(2, t[1][7]); + DRMP3_VSAVE4(3, t[3][7]); + } + } else +#endif +#ifdef DR_MP3_ONLY_SIMD + {} +#else + for (; k < n; k++) + { + float t[4][8], *x, *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + float x0 = y[i*18]; + float x1 = y[(15 - i)*18]; + float x2 = y[(16 + i)*18]; + float x3 = y[(31 - i)*18]; + float t0 = x0 + x3; + float t1 = x1 + x2; + float t2 = (x1 - x2)*g_sec[3*i + 0]; + float t3 = (x0 - x3)*g_sec[3*i + 1]; + x[0] = t0 + t1; + x[8] = (t0 - t1)*g_sec[3*i + 2]; + x[16] = t3 + t2; + x[24] = (t3 - t2)*g_sec[3*i + 2]; + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = x0 - x7; x0 += x7; + x7 = x1 - x6; x1 += x6; + x6 = x2 - x5; x2 += x5; + x5 = x3 - x4; x3 += x4; + x4 = x0 - x3; x0 += x3; + x3 = x1 - x2; x1 += x2; + x[0] = x0 + x1; + x[4] = (x0 - x1)*0.70710677f; + x5 = x5 + x6; + x6 = (x6 + x7)*0.70710677f; + x7 = x7 + xt; + x3 = (x3 + x4)*0.70710677f; + x5 -= x7*0.198912367f; /* rotate by PI/8 */ + x7 += x5*0.382683432f; + x5 -= x7*0.198912367f; + x0 = xt - x6; xt += x6; + x[1] = (xt + x7)*0.50979561f; + x[2] = (x4 + x3)*0.54119611f; + x[3] = (x0 - x5)*0.60134488f; + x[5] = (x0 + x5)*0.89997619f; + x[6] = (x4 - x3)*1.30656302f; + x[7] = (xt - x7)*2.56291556f; + + } + for (i = 0; i < 7; i++, y += 4*18) + { + y[0*18] = t[0][i]; + y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; + y[2*18] = t[1][i] + t[1][i + 1]; + y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; + } + y[0*18] = t[0][7]; + y[1*18] = t[2][7] + t[3][7]; + y[2*18] = t[1][7]; + y[3*18] = t[3][7]; + } +#endif +} + +static short drmp3d_scale_pcm(float sample) +{ + if (sample > 32767.0) return (short) 32767; + if (sample < -32768.0) return (short)-32768; + int s = (int)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + if (s > 32767) return (short) 32767; + if (s < -32768) return (short)-32768; + return (short)s; +} + +static void drmp3d_synth_pair(short *pcm, int nch, const float *z) +{ + float a; + a = (z[14*64] - z[ 0]) * 29; + a += (z[ 1*64] + z[13*64]) * 213; + a += (z[12*64] - z[ 2*64]) * 459; + a += (z[ 3*64] + z[11*64]) * 2037; + a += (z[10*64] - z[ 4*64]) * 5153; + a += (z[ 5*64] + z[ 9*64]) * 6574; + a += (z[ 8*64] - z[ 6*64]) * 37489; + a += z[ 7*64] * 75038; + pcm[0] = drmp3d_scale_pcm(a); + + z += 2; + a = z[14*64] * 104; + a += z[12*64] * 1567; + a += z[10*64] * 9727; + a += z[ 8*64] * 64019; + a += z[ 6*64] * -9975; + a += z[ 4*64] * -45; + a += z[ 2*64] * 146; + a += z[ 0*64] * -5; + pcm[16*nch] = drmp3d_scale_pcm(a); +} + +static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) +{ + int i; + float *xr = xl + 576*(nch - 1); + short *dstr = dstl + (nch - 1); + + static const float g_win[] = { + -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, + -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, + -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, + -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, + -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, + -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, + -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, + -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, + -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, + -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, + -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, + -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, + -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, + -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, + -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 + }; + float *zlin = lins + 15*64; + const float *w = g_win; + + zlin[4*15] = xl[18*16]; + zlin[4*15 + 1] = xr[18*16]; + zlin[4*15 + 2] = xl[0]; + zlin[4*15 + 3] = xr[0]; + + zlin[4*31] = xl[1 + 18*16]; + zlin[4*31 + 1] = xr[1 + 18*16]; + zlin[4*31 + 2] = xl[1]; + zlin[4*31 + 3] = xr[1]; + + drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + drmp3d_synth_pair(dstl, nch, lins + 4*15); + drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); + +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (i = 14; i >= 0; i--) + { +#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); +#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } +#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } +#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } + drmp3_f4 a, b; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*i + 64] = xl[1 + 18*(1 + i)]; + zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; + zlin[4*i - 64 + 2] = xl[18*(1 + i)]; + zlin[4*i - 64 + 3] = xr[18*(1 + i)]; + + DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) + + { +#if DRMP3_HAVE_SSE + static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + dstr[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 6); +#else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); + vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); + vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); + vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); + vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); + vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); + vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); + vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); +#endif + } + } else +#endif +#ifdef DR_MP3_ONLY_SIMD + {} +#else + for (i = 14; i >= 0; i--) + { +#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } + float a[4], b[4]; + + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; + zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; + zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; + zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; + + DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) + + dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); + } +#endif +} + +static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, short *pcm, float *lins) +{ + int i; + for (i = 0; i < nch; i++) + { + drmp3d_DCT_II(grbuf + 576*i, nbands); + } + + memcpy(lins, qmf_state, sizeof(float)*15*64); + + for (i = 0; i < nbands; i += 2) + { + drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + } +#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL + if (nch == 1) + { + for (i = 0; i < 15*64; i += 2) + { + qmf_state[i] = lins[nbands*64 + i]; + } + } else +#endif + { + memcpy(qmf_state, lins + nbands*64, sizeof(float)*15*64); + } +} + +static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) +{ + int i, nmatch; + for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + { + i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); + if (i + DRMP3_HDR_SIZE > mp3_bytes) + return nmatch > 0; + if (!drmp3_hdr_compare(hdr, hdr + i)) + return 0; + } + return 1; +} + +static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +{ + int i, k; + for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) + { + if (drmp3_hdr_valid(mp3)) + { + int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); + + for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) + { + if (drmp3_hdr_compare(mp3, mp3 + k)) + { + int fb = k - drmp3_hdr_padding(mp3); + int nextfb = fb + drmp3_hdr_padding(mp3 + k); + if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) + continue; + frame_and_padding = k; + frame_bytes = fb; + *free_format_bytes = fb; + } + } + + if ((frame_bytes && i + frame_and_padding <= mp3_bytes && + drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + (!i && frame_and_padding == mp3_bytes)) + { + *ptr_frame_bytes = frame_and_padding; + return i; + } + *free_format_bytes = 0; + } + } + *ptr_frame_bytes = 0; + return i; +} + +void drmp3dec_init(drmp3dec *dec) +{ + dec->header[0] = 0; +} + +int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info) +{ + int i = 0, igr, frame_size = 0, success = 1; + const drmp3_uint8 *hdr; + drmp3_bs bs_frame[1]; + drmp3dec_scratch scratch; + + if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) + { + frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) + { + frame_size = 0; + } + } + if (!frame_size) + { + memset(dec, 0, sizeof(drmp3dec)); + i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + if (!frame_size || i + frame_size > mp3_bytes) + { + info->frame_bytes = i; + return 0; + } + } + + hdr = mp3 + i; + memcpy(dec->header, hdr, DRMP3_HDR_SIZE); + info->frame_bytes = i + frame_size; + info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = drmp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); + + drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); + if (DRMP3_HDR_IS_CRC(hdr)) + { + drmp3_bs_get_bits(bs_frame, 16); + } + + if (info->layer == 3) + { + int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + if (success) + { + for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm += 576*info->channels) + { + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, pcm, scratch.syn[0]); + } + } + drmp3_L3_save_reservoir(dec, &scratch); + } else + { +#ifdef DR_MP3_ONLY_MP3 + return 0; +#else + drmp3_L12_scale_info sci[1]; + drmp3_L12_read_scale_info(hdr, bs_frame, sci); + + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + for (i = 0, igr = 0; igr < 3; igr++) + { + if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + { + i = 0; + drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, pcm, scratch.syn[0]); + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + pcm += 384*info->channels; + } + if (bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + } +#endif + } + return success*drmp3_hdr_frame_samples(dec->header); +} + + + + +/* + * + * Main Public API + * + */ + +/* Options. */ +#ifndef DR_MP3_DEFAULT_CHANNELS +#define DR_MP3_DEFAULT_CHANNELS 2 +#endif +#ifndef DR_MP3_DEFAULT_SAMPLE_RATE +#define DR_MP3_DEFAULT_SAMPLE_RATE 44100 +#endif + +/* Standard library stuff. */ +#ifndef DRMP3_ASSERT +#include +#define DRMP3_ASSERT(expression) assert(expression) +#endif +#ifndef DRMP3_COPY_MEMORY +#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRMP3_ZERO_MEMORY +#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef DRMP3_MALLOC +#define DRMP3_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRMP3_REALLOC +#define DRMP3_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRMP3_FREE +#define DRMP3_FREE(p) free((p)) +#endif + +#define drmp3_assert DRMP3_ASSERT +#define drmp3_copy_memory DRMP3_COPY_MEMORY +#define drmp3_zero_memory DRMP3_ZERO_MEMORY +#define drmp3_zero_object DRMP3_ZERO_OBJECT +#define drmp3_malloc DRMP3_MALLOC +#define drmp3_realloc DRMP3_REALLOC + +#define drmp3_countof(x) (sizeof(x) / sizeof(x[0])) +#define drmp3_max(x, y) (((x) > (y)) ? (x) : (y)) +#define drmp3_min(x, y) (((x) < (y)) ? (x) : (y)) + +#define DRMP3_DATA_CHUNK_SIZE 16384 /* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends 16K. */ + +static inline float drmp3_mix_f32(float x, float y, float a) +{ + return x*(1-a) + y*a; +} + +static void drmp3_blend_f32(float* pOut, float* pInA, float* pInB, float factor, drmp3_uint32 channels) +{ + for (drmp3_uint32 i = 0; i < channels; ++i) + pOut[i] = drmp3_mix_f32(pInA[i], pInB[i], factor); +} + +void drmp3_src_cache_init(drmp3_src* pSRC, drmp3_src_cache* pCache) +{ + drmp3_assert(pSRC != NULL); + drmp3_assert(pCache != NULL); + + pCache->pSRC = pSRC; + pCache->cachedFrameCount = 0; + pCache->iNextFrame = 0; +} + +drmp3_uint64 drmp3_src_cache_read_frames(drmp3_src_cache* pCache, drmp3_uint64 frameCount, float* pFramesOut) +{ + drmp3_assert(pCache != NULL); + drmp3_assert(pCache->pSRC != NULL); + drmp3_assert(pCache->pSRC->onRead != NULL); + drmp3_assert(frameCount > 0); + drmp3_assert(pFramesOut != NULL); + + drmp3_uint32 channels = pCache->pSRC->config.channels; + + drmp3_uint64 totalFramesRead = 0; + while (frameCount > 0) + { + /* If there's anything in memory go ahead and copy that over first. */ + drmp3_uint64 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame; + drmp3_uint64 framesToReadFromMemory = frameCount; + if (framesToReadFromMemory > framesRemainingInMemory) + framesToReadFromMemory = framesRemainingInMemory; + + drmp3_copy_memory(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, (drmp3_uint32)(framesToReadFromMemory * channels * sizeof(float))); + pCache->iNextFrame += (drmp3_uint32)framesToReadFromMemory; + + totalFramesRead += framesToReadFromMemory; + frameCount -= framesToReadFromMemory; + if (frameCount == 0) + break; + + + /* At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data. */ + drmp3_assert(frameCount > 0); + pFramesOut += framesToReadFromMemory * channels; + + pCache->iNextFrame = 0; + pCache->cachedFrameCount = 0; + + drmp3_uint32 framesToReadFromClient = drmp3_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels; + if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) + framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; + + pCache->cachedFrameCount = (drmp3_uint32)pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData); + + + /* Get out of this loop if nothing was able to be retrieved. */ + if (pCache->cachedFrameCount == 0) + break; + } + + return totalFramesRead; +} + + +drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush); +drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush); + +drmp3_bool32 drmp3_src_init(const drmp3_src_config* pConfig, drmp3_src_read_proc onRead, void* pUserData, drmp3_src* pSRC) +{ + if (pSRC == NULL) return DRMP3_FALSE; + drmp3_zero_object(pSRC); + + if (pConfig == NULL || onRead == NULL) return DRMP3_FALSE; + if (pConfig->channels == 0 || pConfig->channels > 2) return DRMP3_FALSE; + + pSRC->config = *pConfig; + pSRC->onRead = onRead; + pSRC->pUserData = pUserData; + + if (pSRC->config.cacheSizeInFrames > DRMP3_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) + pSRC->config.cacheSizeInFrames = DRMP3_SRC_CACHE_SIZE_IN_FRAMES; + + drmp3_src_cache_init(pSRC, &pSRC->cache); + return DRMP3_TRUE; +} + +drmp3_bool32 drmp3_src_set_input_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateIn) +{ + if (pSRC == NULL) return DRMP3_FALSE; + + /* Must have a sample rate of > 0. */ + if (sampleRateIn == 0) + return DRMP3_FALSE; + + pSRC->config.sampleRateIn = sampleRateIn; + return DRMP3_TRUE; +} + +drmp3_bool32 drmp3_src_set_output_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateOut) +{ + if (pSRC == NULL) return DRMP3_FALSE; + + /* Must have a sample rate of > 0. */ + if (sampleRateOut == 0) + return DRMP3_FALSE; + + pSRC->config.sampleRateOut = sampleRateOut; + return DRMP3_TRUE; +} + +drmp3_uint64 drmp3_src_read_frames_ex(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) +{ + if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0; + + drmp3_src_algorithm algorithm = pSRC->config.algorithm; + + /* Always use passthrough if the sample rates are the same. */ + if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) + algorithm = drmp3_src_algorithm_none; + + /* Could just use a function pointer instead of a switch for this... */ + switch (algorithm) + { + case drmp3_src_algorithm_none: return drmp3_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush); + case drmp3_src_algorithm_linear: return drmp3_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush); + default: return 0; + } +} + +drmp3_uint64 drmp3_src_read_frames(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut) +{ + return drmp3_src_read_frames_ex(pSRC, frameCount, pFramesOut, DRMP3_FALSE); +} + +drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) +{ + drmp3_assert(pSRC != NULL); + drmp3_assert(frameCount > 0); + drmp3_assert(pFramesOut != NULL); + + (void)flush; /* Passthrough need not care about flushing. */ + return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData); +} + +drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) +{ + drmp3_assert(pSRC != NULL); + drmp3_assert(frameCount > 0); + drmp3_assert(pFramesOut != NULL); + + /* For linear SRC, the bin is only 2 frames: 1 prior, 1 future. */ + + /* Load the bin if necessary. */ + if (!pSRC->algo.linear.isPrevFramesLoaded) + { + drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin); + if (framesRead == 0) + return 0; + pSRC->algo.linear.isPrevFramesLoaded = DRMP3_TRUE; + } + if (!pSRC->algo.linear.isNextFramesLoaded) + { + drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin + pSRC->config.channels); + if (framesRead == 0) + return 0; + pSRC->algo.linear.isNextFramesLoaded = DRMP3_TRUE; + } + + float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; + + drmp3_uint64 totalFramesRead = 0; + while (frameCount > 0) + { + /* The bin is where the previous and next frames are located. */ + float* pPrevFrame = pSRC->bin; + float* pNextFrame = pSRC->bin + pSRC->config.channels; + + drmp3_blend_f32((float*)pFramesOut, pPrevFrame, pNextFrame, pSRC->algo.linear.alpha, pSRC->config.channels); + + pSRC->algo.linear.alpha += factor; + + /* The new alpha value is how we determine whether or not we need to read fresh frames. */ + drmp3_uint32 framesToReadFromClient = (drmp3_uint32)pSRC->algo.linear.alpha; + pSRC->algo.linear.alpha = pSRC->algo.linear.alpha - framesToReadFromClient; + + for (drmp3_uint32 i = 0; i < framesToReadFromClient; ++i) + { + for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) + pPrevFrame[j] = pNextFrame[j]; + + drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pNextFrame); + if (framesRead == 0) + { + for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) + pNextFrame[j] = 0; + + if (pSRC->algo.linear.isNextFramesLoaded) + pSRC->algo.linear.isNextFramesLoaded = DRMP3_FALSE; + else + { + if (flush) + pSRC->algo.linear.isPrevFramesLoaded = DRMP3_FALSE; + } + + break; + } + } + + pFramesOut = (drmp3_uint8*)pFramesOut + (1 * pSRC->config.channels * sizeof(float)); + frameCount -= 1; + totalFramesRead += 1; + + /* If there's no frames available we need to get out of this loop. */ + if (!pSRC->algo.linear.isNextFramesLoaded && (!flush || !pSRC->algo.linear.isPrevFramesLoaded)) + break; + } + + return totalFramesRead; +} + + + +static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3) +{ + drmp3_assert(pMP3 != NULL); + drmp3_assert(pMP3->onRead != NULL); + + if (pMP3->atEnd) + return DRMP3_FALSE; + + do + { + /* minimp3 recommends doing data submission in 16K chunks. If we don't have at least 16K bytes available, get more. */ + if (pMP3->dataSize < DRMP3_DATA_CHUNK_SIZE) + { + if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) + { + pMP3->dataCapacity = DRMP3_DATA_CHUNK_SIZE; + drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity); + if (pNewData == NULL) + return DRMP3_FALSE; /* Out of memory. */ + + pMP3->pData = pNewData; + } + + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) + { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; /* No data. */ + } + + pMP3->dataSize += bytesRead; + } + + if (pMP3->dataSize > INT_MAX) + { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; /* File too big. */ + } + + drmp3dec_frame_info info; + drmp3_uint32 samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pMP3->frames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ + if (samplesRead != 0) + { + size_t i; + size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes); + for (i = 0; i < leftoverDataSize; ++i) + pMP3->pData[i] = pMP3->pData[i + (size_t)info.frame_bytes]; + + pMP3->dataSize = leftoverDataSize; + pMP3->framesConsumed = 0; + pMP3->framesRemaining = samplesRead; + pMP3->frameChannels = info.channels; + pMP3->frameSampleRate = info.hz; + drmp3_src_set_input_sample_rate(&pMP3->src, pMP3->frameSampleRate); + break; + } else { + /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ + if (pMP3->dataCapacity == pMP3->dataSize) + { + /* No room. Expand. */ + pMP3->dataCapacity += DRMP3_DATA_CHUNK_SIZE; + drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity); + if (pNewData == NULL) + return DRMP3_FALSE; /* Out of memory. */ + + pMP3->pData = pNewData; + } + + /* Fill in a chunk. */ + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) + { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; /* Error reading more data. */ + } + + pMP3->dataSize += bytesRead; + } + } while (DRMP3_TRUE); + + return DRMP3_TRUE; +} + +static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData) +{ + drmp3* pMP3 = (drmp3*)pUserData; + drmp3_assert(pMP3 != NULL); + drmp3_assert(pMP3->onRead != NULL); + + float* pFramesOutF = (float*)pFramesOut; + drmp3_uint32 totalFramesRead = 0; + + while (frameCount > 0) + { + /* Read from the in-memory buffer first. */ + while (pMP3->framesRemaining > 0 && frameCount > 0) + { + if (pMP3->frameChannels == 1) + { + if (pMP3->channels == 1) + { + /* Mono -> Mono. */ + pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + } else { + /* Mono -> Stereo. */ + pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + pFramesOutF[1] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + } + } else { + if (pMP3->channels == 1) + { + /* Stereo -> Mono */ + float sample = 0; + sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + pFramesOutF[0] = sample * 0.5f; + } else { + /* Stereo -> Stereo */ + pFramesOutF[0] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + pFramesOutF[1] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + } + } + + pMP3->framesConsumed += 1; + pMP3->framesRemaining -= 1; + frameCount -= 1; + totalFramesRead += 1; + pFramesOutF += pSRC->config.channels; + } + + if (frameCount == 0) + { + break; + } + + drmp3_assert(pMP3->framesRemaining == 0); + + /* At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed + * at this point which means we'll also need to update our sample rate conversion pipeline. */ + if (!drmp3_decode_next_frame(pMP3)) + break; + } + + return totalFramesRead; +} + +drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig) +{ + drmp3_assert(pMP3 != NULL); + drmp3_assert(onRead != NULL); + + /* This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break. */ + drmp3dec_init(&pMP3->decoder); + + /* The config can be null in which case we use defaults. */ + drmp3_config config; + if (pConfig != NULL) + config = *pConfig; + else + drmp3_zero_object(&config); + + pMP3->channels = config.outputChannels; + if (pMP3->channels == 0) + pMP3->channels = DR_MP3_DEFAULT_CHANNELS; + + /* Cannot have more than 2 channels. */ + if (pMP3->channels > 2) + pMP3->channels = 2; + + pMP3->sampleRate = config.outputSampleRate; + if (pMP3->sampleRate == 0) + pMP3->sampleRate = DR_MP3_DEFAULT_SAMPLE_RATE; + + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->pUserData = pUserData; + + /* We need a sample rate converter for converting the sample rate from the MP3 frames to the requested output sample rate. */ + drmp3_src_config srcConfig; + drmp3_zero_object(&srcConfig); + srcConfig.sampleRateIn = DR_MP3_DEFAULT_SAMPLE_RATE; + srcConfig.sampleRateOut = pMP3->sampleRate; + srcConfig.channels = pMP3->channels; + srcConfig.algorithm = drmp3_src_algorithm_linear; + if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) + return DRMP3_FALSE; + + /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */ + if (!drmp3_decode_next_frame(pMP3)) + return DRMP3_FALSE; /* Not a valid MP3 stream. */ + + return DRMP3_TRUE; +} + +drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig) +{ + if (pMP3 == NULL || onRead == NULL) + return DRMP3_FALSE; + + drmp3_zero_object(pMP3); + return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pConfig); +} + + +static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + size_t bytesRemaining; + drmp3* pMP3 = (drmp3*)pUserData; + drmp3_assert(pMP3 != NULL); + drmp3_assert(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + + bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; + if (bytesToRead > bytesRemaining) + bytesToRead = bytesRemaining; + + if (bytesToRead > 0) + { + drmp3_copy_memory(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); + pMP3->memory.currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) +{ + drmp3* pMP3 = (drmp3*)pUserData; + drmp3_assert(pMP3 != NULL); + + if (origin == drmp3_seek_origin_current) + { + if (byteOffset > 0) + { + if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) + byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); /* Trying to seek too far forward. */ + } + else + { + if (pMP3->memory.currentReadPos < (size_t)-byteOffset) + byteOffset = -(int)pMP3->memory.currentReadPos; /* Trying to seek too far backwards. */ + } + + /* This will never underflow thanks to the clamps above. */ + pMP3->memory.currentReadPos += byteOffset; + } else { + if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) + pMP3->memory.currentReadPos = byteOffset; + else + pMP3->memory.currentReadPos = pMP3->memory.dataSize; /* Trying to seek too far forward. */ + } + + return DRMP3_TRUE; +} + +drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_config* pConfig) +{ + if (pMP3 == NULL) + return DRMP3_FALSE; + + drmp3_zero_object(pMP3); + + if (pData == NULL || dataSize == 0) + return DRMP3_FALSE; + + pMP3->memory.pData = (const drmp3_uint8*)pData; + pMP3->memory.dataSize = dataSize; + pMP3->memory.currentReadPos = 0; + + return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pConfig); +} + + +#ifndef DR_MP3_NO_STDIO +#include + +static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig) +{ + FILE* pFile; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (fopen_s(&pFile, filePath, "rb") != 0) + return DRMP3_FALSE; +#else + pFile = fopen(filePath, "rb"); + if (pFile == NULL) + return DRMP3_FALSE; +#endif + + return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pConfig); +} +#endif + +void drmp3_uninit(drmp3* pMP3) +{ + if (pMP3 == NULL) return; + +#ifndef DR_MP3_NO_STDIO + if (pMP3->onRead == drmp3__on_read_stdio) + fclose((FILE*)pMP3->pUserData); +#endif + + drmp3_free(pMP3->pData); +} + +drmp3_uint64 drmp3_read_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) return 0; + + drmp3_uint64 totalFramesRead = 0; + + if (pBufferOut == NULL) + { + float temp[4096]; + while (framesToRead > 0) + { + drmp3_uint64 framesToReadRightNow = sizeof(temp)/sizeof(temp[0]) / pMP3->channels; + if (framesToReadRightNow > framesToRead) + framesToReadRightNow = framesToRead; + + drmp3_uint64 framesJustRead = drmp3_read_f32(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) + break; + + framesToRead -= framesJustRead; + totalFramesRead += framesJustRead; + } + } else { + totalFramesRead = drmp3_src_read_frames_ex(&pMP3->src, framesToRead, pBufferOut, DRMP3_TRUE); + } + + return totalFramesRead; +} + +drmp3_bool32 drmp3_seek_to_frame(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + if (pMP3 == NULL || pMP3->onSeek == NULL) return DRMP3_FALSE; + + /* Seek to the start of the stream to begin with. */ + if (!pMP3->onSeek(pMP3->pUserData, 0, drmp3_seek_origin_start)) + return DRMP3_FALSE; + + /* Clear any cached data. */ + pMP3->framesConsumed = 0; + pMP3->framesRemaining = 0; + pMP3->dataSize = 0; + pMP3->atEnd = DRMP3_FALSE; + + /* TODO: Optimize. + * + * This is inefficient. We simply read frames from the start of the stream. */ + drmp3_uint64 framesRead = drmp3_read_f32(pMP3, frameIndex, NULL); + if (framesRead != frameIndex) + return DRMP3_FALSE; + + return DRMP3_TRUE; +} + + + +float* drmp3__full_decode_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3_assert(pMP3 != NULL); + + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; + float* pFrames = NULL; + + float temp[4096]; + for (;;) + { + drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_f32(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) + break; + + /* Reallocate the output buffer if there's not enough room. */ + if (framesCapacity < totalFramesRead + framesJustRead) + { + framesCapacity *= 2; + if (framesCapacity < totalFramesRead + framesJustRead) + framesCapacity = totalFramesRead + framesJustRead; + + drmp3_uint64 newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float); + if (newFramesBufferSize > SIZE_MAX) + break; + + float* pNewFrames = (float*)drmp3_realloc(pFrames, (size_t)newFramesBufferSize); + if (pNewFrames == NULL) + { + drmp3_free(pFrames); + break; + } + + pFrames = pNewFrames; + } + + drmp3_copy_memory(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); + totalFramesRead += framesJustRead; + + /* If the number of frames we asked for is less that what we actually read it means we've reached the end. */ + if (framesJustRead != framesToReadRightNow) + break; + } + + if (pConfig != NULL) + { + pConfig->outputChannels = pMP3->channels; + pConfig->outputSampleRate = pMP3->sampleRate; + } + + drmp3_uninit(pMP3); + + if (pTotalFrameCount) *pTotalFrameCount = totalFramesRead; + return pFrames; +} + +float* drmp3_open_and_decode_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pConfig)) + return NULL; + + return drmp3__full_decode_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +float* drmp3_open_and_decode_memory_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pConfig)) + return NULL; + + return drmp3__full_decode_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +#ifndef DR_MP3_NO_STDIO +float* drmp3_open_and_decode_file_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pConfig)) + return NULL; + + return drmp3__full_decode_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} +#endif + +void drmp3_free(void* p) +{ + DRMP3_FREE(p); +} + +#endif /*DR_MP3_IMPLEMENTATION*/ + +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to +*/ + +/* + https://github.com/lieff/minimp3 + To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. + This software is distributed without any warranty. + See . +*/ From e3ce768ad0b3cf696434e485268b2586a4dc91e2 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 14:32:10 +0200 Subject: [PATCH 510/517] (dr_mp3.h) Fix C89_BUILD issues --- deps/dr/dr_mp3.h | 595 +++++++++++++++++++++++++---------------------- 1 file changed, 314 insertions(+), 281 deletions(-) diff --git a/deps/dr/dr_mp3.h b/deps/dr/dr_mp3.h index 01483843f9..9b74cad353 100644 --- a/deps/dr/dr_mp3.h +++ b/deps/dr/dr_mp3.h @@ -9,6 +9,7 @@ extern "C" { #include #include +#include typedef int8_t drmp3_int8; typedef uint8_t drmp3_uint8; @@ -312,7 +313,7 @@ typedef __m128 drmp3_f4; #if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) #define drmp3_cpuid __cpuid #else -static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) +static INLINE __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) { #if defined(__PIC__) __asm__ __volatile__( @@ -1600,13 +1601,14 @@ static void drmp3d_DCT_II(float *grbuf, int n) static short drmp3d_scale_pcm(float sample) { - if (sample > 32767.0) return (short) 32767; - if (sample < -32768.0) return (short)-32768; - int s = (int)(sample + .5f); - s -= (s < 0); /* away from zero, to be compliant */ - if (s > 32767) return (short) 32767; - if (s < -32768) return (short)-32768; - return (short)s; + int s; + if (sample > 32767.0) return (short) 32767; + if (sample < -32768.0) return (short)-32768; + s = (int)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + if (s > 32767) return (short) 32767; + if (s < -32768) return (short)-32768; + return (short)s; } static void drmp3d_synth_pair(short *pcm, int nch, const float *z) @@ -1986,14 +1988,15 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes #define DRMP3_DATA_CHUNK_SIZE 16384 /* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends 16K. */ -static inline float drmp3_mix_f32(float x, float y, float a) +static INLINE float drmp3_mix_f32(float x, float y, float a) { return x*(1-a) + y*a; } static void drmp3_blend_f32(float* pOut, float* pInA, float* pInB, float factor, drmp3_uint32 channels) { - for (drmp3_uint32 i = 0; i < channels; ++i) + uint32_t i; + for (i = 0; i < channels; ++i) pOut[i] = drmp3_mix_f32(pInA[i], pInB[i], factor); } @@ -2009,52 +2012,55 @@ void drmp3_src_cache_init(drmp3_src* pSRC, drmp3_src_cache* pCache) drmp3_uint64 drmp3_src_cache_read_frames(drmp3_src_cache* pCache, drmp3_uint64 frameCount, float* pFramesOut) { - drmp3_assert(pCache != NULL); - drmp3_assert(pCache->pSRC != NULL); - drmp3_assert(pCache->pSRC->onRead != NULL); - drmp3_assert(frameCount > 0); - drmp3_assert(pFramesOut != NULL); + drmp3_uint32 channels; + drmp3_uint64 totalFramesRead = 0; - drmp3_uint32 channels = pCache->pSRC->config.channels; + drmp3_assert(pCache != NULL); + drmp3_assert(pCache->pSRC != NULL); + drmp3_assert(pCache->pSRC->onRead != NULL); + drmp3_assert(frameCount > 0); + drmp3_assert(pFramesOut != NULL); - drmp3_uint64 totalFramesRead = 0; - while (frameCount > 0) - { - /* If there's anything in memory go ahead and copy that over first. */ - drmp3_uint64 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame; - drmp3_uint64 framesToReadFromMemory = frameCount; - if (framesToReadFromMemory > framesRemainingInMemory) - framesToReadFromMemory = framesRemainingInMemory; + channels = pCache->pSRC->config.channels; - drmp3_copy_memory(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, (drmp3_uint32)(framesToReadFromMemory * channels * sizeof(float))); - pCache->iNextFrame += (drmp3_uint32)framesToReadFromMemory; + while (frameCount > 0) + { + drmp3_uint32 framesToReadFromClient; + /* If there's anything in memory go ahead and copy that over first. */ + drmp3_uint64 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame; + drmp3_uint64 framesToReadFromMemory = frameCount; + if (framesToReadFromMemory > framesRemainingInMemory) + framesToReadFromMemory = framesRemainingInMemory; - totalFramesRead += framesToReadFromMemory; - frameCount -= framesToReadFromMemory; - if (frameCount == 0) - break; + drmp3_copy_memory(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, (drmp3_uint32)(framesToReadFromMemory * channels * sizeof(float))); + pCache->iNextFrame += (drmp3_uint32)framesToReadFromMemory; + + totalFramesRead += framesToReadFromMemory; + frameCount -= framesToReadFromMemory; + if (frameCount == 0) + break; - /* At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data. */ - drmp3_assert(frameCount > 0); - pFramesOut += framesToReadFromMemory * channels; + /* At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data. */ + drmp3_assert(frameCount > 0); + pFramesOut += framesToReadFromMemory * channels; - pCache->iNextFrame = 0; - pCache->cachedFrameCount = 0; + pCache->iNextFrame = 0; + pCache->cachedFrameCount = 0; - drmp3_uint32 framesToReadFromClient = drmp3_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels; - if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) - framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; + framesToReadFromClient = drmp3_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels; + if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) + framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; - pCache->cachedFrameCount = (drmp3_uint32)pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData); + pCache->cachedFrameCount = (drmp3_uint32)pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData); - /* Get out of this loop if nothing was able to be retrieved. */ - if (pCache->cachedFrameCount == 0) - break; - } + /* Get out of this loop if nothing was able to be retrieved. */ + if (pCache->cachedFrameCount == 0) + break; + } - return totalFramesRead; + return totalFramesRead; } @@ -2106,21 +2112,22 @@ drmp3_bool32 drmp3_src_set_output_sample_rate(drmp3_src* pSRC, drmp3_uint32 samp drmp3_uint64 drmp3_src_read_frames_ex(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) { - if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0; + drmp3_src_algorithm algorithm; + if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0; - drmp3_src_algorithm algorithm = pSRC->config.algorithm; + algorithm = pSRC->config.algorithm; - /* Always use passthrough if the sample rates are the same. */ - if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) - algorithm = drmp3_src_algorithm_none; + /* Always use passthrough if the sample rates are the same. */ + if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) + algorithm = drmp3_src_algorithm_none; - /* Could just use a function pointer instead of a switch for this... */ - switch (algorithm) - { - case drmp3_src_algorithm_none: return drmp3_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush); - case drmp3_src_algorithm_linear: return drmp3_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush); - default: return 0; - } + /* Could just use a function pointer instead of a switch for this... */ + switch (algorithm) + { + case drmp3_src_algorithm_none: return drmp3_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush); + case drmp3_src_algorithm_linear: return drmp3_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush); + default: return 0; + } } drmp3_uint64 drmp3_src_read_frames(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut) @@ -2140,78 +2147,85 @@ drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 fra drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) { - drmp3_assert(pSRC != NULL); - drmp3_assert(frameCount > 0); - drmp3_assert(pFramesOut != NULL); + float factor; + drmp3_uint64 totalFramesRead = 0; - /* For linear SRC, the bin is only 2 frames: 1 prior, 1 future. */ + drmp3_assert(pSRC != NULL); + drmp3_assert(frameCount > 0); + drmp3_assert(pFramesOut != NULL); - /* Load the bin if necessary. */ - if (!pSRC->algo.linear.isPrevFramesLoaded) - { - drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin); - if (framesRead == 0) - return 0; - pSRC->algo.linear.isPrevFramesLoaded = DRMP3_TRUE; - } - if (!pSRC->algo.linear.isNextFramesLoaded) - { - drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin + pSRC->config.channels); - if (framesRead == 0) - return 0; - pSRC->algo.linear.isNextFramesLoaded = DRMP3_TRUE; - } + /* For linear SRC, the bin is only 2 frames: 1 prior, 1 future. */ - float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; + /* Load the bin if necessary. */ + if (!pSRC->algo.linear.isPrevFramesLoaded) + { + drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin); + if (framesRead == 0) + return 0; + pSRC->algo.linear.isPrevFramesLoaded = DRMP3_TRUE; + } + if (!pSRC->algo.linear.isNextFramesLoaded) + { + drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin + pSRC->config.channels); + if (framesRead == 0) + return 0; + pSRC->algo.linear.isNextFramesLoaded = DRMP3_TRUE; + } - drmp3_uint64 totalFramesRead = 0; - while (frameCount > 0) - { - /* The bin is where the previous and next frames are located. */ - float* pPrevFrame = pSRC->bin; - float* pNextFrame = pSRC->bin + pSRC->config.channels; + factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; - drmp3_blend_f32((float*)pFramesOut, pPrevFrame, pNextFrame, pSRC->algo.linear.alpha, pSRC->config.channels); + while (frameCount > 0) + { + drmp3_uint32 i; + drmp3_uint32 framesToReadFromClient; + /* The bin is where the previous and next frames are located. */ + float* pPrevFrame = pSRC->bin; + float* pNextFrame = pSRC->bin + pSRC->config.channels; - pSRC->algo.linear.alpha += factor; + drmp3_blend_f32((float*)pFramesOut, pPrevFrame, pNextFrame, pSRC->algo.linear.alpha, pSRC->config.channels); - /* The new alpha value is how we determine whether or not we need to read fresh frames. */ - drmp3_uint32 framesToReadFromClient = (drmp3_uint32)pSRC->algo.linear.alpha; - pSRC->algo.linear.alpha = pSRC->algo.linear.alpha - framesToReadFromClient; + pSRC->algo.linear.alpha += factor; - for (drmp3_uint32 i = 0; i < framesToReadFromClient; ++i) - { - for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) - pPrevFrame[j] = pNextFrame[j]; + /* The new alpha value is how we determine whether or not we need to read fresh frames. */ + framesToReadFromClient = (drmp3_uint32)pSRC->algo.linear.alpha; + pSRC->algo.linear.alpha = pSRC->algo.linear.alpha - framesToReadFromClient; - drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pNextFrame); - if (framesRead == 0) + for (i = 0; i < framesToReadFromClient; ++i) + { + drmp3_uint32 j; + drmp3_uint64 framesRead; + for (j = 0; j < pSRC->config.channels; ++j) + pPrevFrame[j] = pNextFrame[j]; + + framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pNextFrame); + if (framesRead == 0) + { + drmp3_uint32 j; + for (j = 0; j < pSRC->config.channels; ++j) + pNextFrame[j] = 0; + + if (pSRC->algo.linear.isNextFramesLoaded) + pSRC->algo.linear.isNextFramesLoaded = DRMP3_FALSE; + else { - for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) - pNextFrame[j] = 0; - - if (pSRC->algo.linear.isNextFramesLoaded) - pSRC->algo.linear.isNextFramesLoaded = DRMP3_FALSE; - else - { - if (flush) - pSRC->algo.linear.isPrevFramesLoaded = DRMP3_FALSE; - } - - break; + if (flush) + pSRC->algo.linear.isPrevFramesLoaded = DRMP3_FALSE; } - } - pFramesOut = (drmp3_uint8*)pFramesOut + (1 * pSRC->config.channels * sizeof(float)); - frameCount -= 1; - totalFramesRead += 1; - - /* If there's no frames available we need to get out of this loop. */ - if (!pSRC->algo.linear.isNextFramesLoaded && (!flush || !pSRC->algo.linear.isPrevFramesLoaded)) break; - } + } + } - return totalFramesRead; + pFramesOut = (drmp3_uint8*)pFramesOut + (1 * pSRC->config.channels * sizeof(float)); + frameCount -= 1; + totalFramesRead += 1; + + /* If there's no frames available we need to get out of this loop. */ + if (!pSRC->algo.linear.isNextFramesLoaded && (!flush || !pSRC->algo.linear.isPrevFramesLoaded)) + break; + } + + return totalFramesRead; } @@ -2226,74 +2240,86 @@ static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3) do { - /* minimp3 recommends doing data submission in 16K chunks. If we don't have at least 16K bytes available, get more. */ - if (pMP3->dataSize < DRMP3_DATA_CHUNK_SIZE) - { - if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) - { - pMP3->dataCapacity = DRMP3_DATA_CHUNK_SIZE; - drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity); - if (pNewData == NULL) - return DRMP3_FALSE; /* Out of memory. */ + drmp3dec_frame_info info; + drmp3_uint32 samplesRead; - pMP3->pData = pNewData; - } + /* minimp3 recommends doing data submission in 16K chunks. If we don't have at least 16K bytes available, get more. */ + if (pMP3->dataSize < DRMP3_DATA_CHUNK_SIZE) + { + size_t bytesRead; - size_t bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) - { - pMP3->atEnd = DRMP3_TRUE; - return DRMP3_FALSE; /* No data. */ - } + if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) + { + drmp3_uint8* pNewData = NULL; + pMP3->dataCapacity = DRMP3_DATA_CHUNK_SIZE; - pMP3->dataSize += bytesRead; - } + pNewData = (drmp3_uint8*) + drmp3_realloc(pMP3->pData, pMP3->dataCapacity); + if (pNewData == NULL) + return DRMP3_FALSE; /* Out of memory. */ - if (pMP3->dataSize > INT_MAX) - { - pMP3->atEnd = DRMP3_TRUE; - return DRMP3_FALSE; /* File too big. */ - } + pMP3->pData = pNewData; + } - drmp3dec_frame_info info; - drmp3_uint32 samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pMP3->frames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ - if (samplesRead != 0) - { - size_t i; - size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes); - for (i = 0; i < leftoverDataSize; ++i) - pMP3->pData[i] = pMP3->pData[i + (size_t)info.frame_bytes]; + bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) + { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; /* No data. */ + } - pMP3->dataSize = leftoverDataSize; - pMP3->framesConsumed = 0; - pMP3->framesRemaining = samplesRead; - pMP3->frameChannels = info.channels; - pMP3->frameSampleRate = info.hz; - drmp3_src_set_input_sample_rate(&pMP3->src, pMP3->frameSampleRate); - break; - } else { - /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ - if (pMP3->dataCapacity == pMP3->dataSize) - { - /* No room. Expand. */ - pMP3->dataCapacity += DRMP3_DATA_CHUNK_SIZE; - drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity); - if (pNewData == NULL) - return DRMP3_FALSE; /* Out of memory. */ + pMP3->dataSize += bytesRead; + } - pMP3->pData = pNewData; - } + if (pMP3->dataSize > INT_MAX) + { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; /* File too big. */ + } - /* Fill in a chunk. */ - size_t bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) - { - pMP3->atEnd = DRMP3_TRUE; - return DRMP3_FALSE; /* Error reading more data. */ - } + samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pMP3->frames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ + if (samplesRead != 0) + { + size_t i; + size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes); + for (i = 0; i < leftoverDataSize; ++i) + pMP3->pData[i] = pMP3->pData[i + (size_t)info.frame_bytes]; - pMP3->dataSize += bytesRead; - } + pMP3->dataSize = leftoverDataSize; + pMP3->framesConsumed = 0; + pMP3->framesRemaining = samplesRead; + pMP3->frameChannels = info.channels; + pMP3->frameSampleRate = info.hz; + drmp3_src_set_input_sample_rate(&pMP3->src, pMP3->frameSampleRate); + break; + } + else + { + size_t bytesRead; + /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ + if (pMP3->dataCapacity == pMP3->dataSize) + { + drmp3_uint8 *pNewData = NULL; + /* No room. Expand. */ + pMP3->dataCapacity += DRMP3_DATA_CHUNK_SIZE; + + pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity); + if (pNewData == NULL) + return DRMP3_FALSE; /* Out of memory. */ + + pMP3->pData = pNewData; + } + + /* Fill in a chunk. */ + bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) + { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; /* Error reading more data. */ + } + + pMP3->dataSize += bytesRead; + } } while (DRMP3_TRUE); return DRMP3_TRUE; @@ -2301,113 +2327,114 @@ static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3) static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData) { - drmp3* pMP3 = (drmp3*)pUserData; - drmp3_assert(pMP3 != NULL); - drmp3_assert(pMP3->onRead != NULL); + float* pFramesOutF; + drmp3_uint32 totalFramesRead = 0; + drmp3* pMP3 = (drmp3*)pUserData; - float* pFramesOutF = (float*)pFramesOut; - drmp3_uint32 totalFramesRead = 0; + drmp3_assert(pMP3 != NULL); + drmp3_assert(pMP3->onRead != NULL); - while (frameCount > 0) - { - /* Read from the in-memory buffer first. */ - while (pMP3->framesRemaining > 0 && frameCount > 0) - { - if (pMP3->frameChannels == 1) + pFramesOutF = (float*)pFramesOut; + + while (frameCount > 0) + { + /* Read from the in-memory buffer first. */ + while (pMP3->framesRemaining > 0 && frameCount > 0) + { + if (pMP3->frameChannels == 1) + { + if (pMP3->channels == 1) { - if (pMP3->channels == 1) - { - /* Mono -> Mono. */ - pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; - } else { - /* Mono -> Stereo. */ - pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; - pFramesOutF[1] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; - } + /* Mono -> Mono. */ + pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; } else { - if (pMP3->channels == 1) - { - /* Stereo -> Mono */ - float sample = 0; - sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; - sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; - pFramesOutF[0] = sample * 0.5f; - } else { - /* Stereo -> Stereo */ - pFramesOutF[0] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; - pFramesOutF[1] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; - } + /* Mono -> Stereo. */ + pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + pFramesOutF[1] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; } + } else { + if (pMP3->channels == 1) + { + /* Stereo -> Mono */ + float sample = 0; + sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + pFramesOutF[0] = sample * 0.5f; + } else { + /* Stereo -> Stereo */ + pFramesOutF[0] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + pFramesOutF[1] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + } + } - pMP3->framesConsumed += 1; - pMP3->framesRemaining -= 1; - frameCount -= 1; - totalFramesRead += 1; - pFramesOutF += pSRC->config.channels; - } + pMP3->framesConsumed += 1; + pMP3->framesRemaining -= 1; + frameCount -= 1; + totalFramesRead += 1; + pFramesOutF += pSRC->config.channels; + } - if (frameCount == 0) - { - break; - } + if (frameCount == 0) + break; - drmp3_assert(pMP3->framesRemaining == 0); + drmp3_assert(pMP3->framesRemaining == 0); - /* At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed - * at this point which means we'll also need to update our sample rate conversion pipeline. */ - if (!drmp3_decode_next_frame(pMP3)) - break; - } + /* At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed + * at this point which means we'll also need to update our sample rate conversion pipeline. */ + if (!drmp3_decode_next_frame(pMP3)) + break; + } - return totalFramesRead; + return totalFramesRead; } drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig) { - drmp3_assert(pMP3 != NULL); - drmp3_assert(onRead != NULL); + drmp3_config config; + drmp3_src_config srcConfig; - /* This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break. */ - drmp3dec_init(&pMP3->decoder); + drmp3_assert(pMP3 != NULL); + drmp3_assert(onRead != NULL); - /* The config can be null in which case we use defaults. */ - drmp3_config config; - if (pConfig != NULL) - config = *pConfig; - else - drmp3_zero_object(&config); + /* This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break. */ + drmp3dec_init(&pMP3->decoder); - pMP3->channels = config.outputChannels; - if (pMP3->channels == 0) - pMP3->channels = DR_MP3_DEFAULT_CHANNELS; + /* The config can be null in which case we use defaults. */ + if (pConfig != NULL) + config = *pConfig; + else + drmp3_zero_object(&config); - /* Cannot have more than 2 channels. */ - if (pMP3->channels > 2) - pMP3->channels = 2; + pMP3->channels = config.outputChannels; + if (pMP3->channels == 0) + pMP3->channels = DR_MP3_DEFAULT_CHANNELS; - pMP3->sampleRate = config.outputSampleRate; - if (pMP3->sampleRate == 0) - pMP3->sampleRate = DR_MP3_DEFAULT_SAMPLE_RATE; + /* Cannot have more than 2 channels. */ + if (pMP3->channels > 2) + pMP3->channels = 2; - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->pUserData = pUserData; + pMP3->sampleRate = config.outputSampleRate; + if (pMP3->sampleRate == 0) + pMP3->sampleRate = DR_MP3_DEFAULT_SAMPLE_RATE; - /* We need a sample rate converter for converting the sample rate from the MP3 frames to the requested output sample rate. */ - drmp3_src_config srcConfig; - drmp3_zero_object(&srcConfig); - srcConfig.sampleRateIn = DR_MP3_DEFAULT_SAMPLE_RATE; - srcConfig.sampleRateOut = pMP3->sampleRate; - srcConfig.channels = pMP3->channels; - srcConfig.algorithm = drmp3_src_algorithm_linear; - if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) - return DRMP3_FALSE; - - /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */ - if (!drmp3_decode_next_frame(pMP3)) - return DRMP3_FALSE; /* Not a valid MP3 stream. */ + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->pUserData = pUserData; - return DRMP3_TRUE; + /* We need a sample rate converter for converting the sample rate from the MP3 frames to the requested output sample rate. */ + drmp3_zero_object(&srcConfig); + srcConfig.sampleRateIn = DR_MP3_DEFAULT_SAMPLE_RATE; + srcConfig.sampleRateOut = pMP3->sampleRate; + srcConfig.channels = pMP3->channels; + srcConfig.algorithm = drmp3_src_algorithm_linear; + if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) + return DRMP3_FALSE; + + /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */ + if (!drmp3_decode_next_frame(pMP3)) + return DRMP3_FALSE; /* Not a valid MP3 stream. */ + + return DRMP3_TRUE; } drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig) @@ -2531,20 +2558,20 @@ void drmp3_uninit(drmp3* pMP3) drmp3_uint64 drmp3_read_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) { - if (pMP3 == NULL || pMP3->onRead == NULL) return 0; - drmp3_uint64 totalFramesRead = 0; + if (pMP3 == NULL || pMP3->onRead == NULL) return 0; if (pBufferOut == NULL) { float temp[4096]; while (framesToRead > 0) { + drmp3_uint64 framesJustRead; drmp3_uint64 framesToReadRightNow = sizeof(temp)/sizeof(temp[0]) / pMP3->channels; if (framesToReadRightNow > framesToRead) framesToReadRightNow = framesToRead; - drmp3_uint64 framesJustRead = drmp3_read_f32(pMP3, framesToReadRightNow, temp); + framesJustRead = drmp3_read_f32(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) break; @@ -2560,39 +2587,42 @@ drmp3_uint64 drmp3_read_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBuff drmp3_bool32 drmp3_seek_to_frame(drmp3* pMP3, drmp3_uint64 frameIndex) { - if (pMP3 == NULL || pMP3->onSeek == NULL) return DRMP3_FALSE; + drmp3_uint64 framesRead; - /* Seek to the start of the stream to begin with. */ - if (!pMP3->onSeek(pMP3->pUserData, 0, drmp3_seek_origin_start)) - return DRMP3_FALSE; + if (pMP3 == NULL || pMP3->onSeek == NULL) return DRMP3_FALSE; - /* Clear any cached data. */ - pMP3->framesConsumed = 0; - pMP3->framesRemaining = 0; - pMP3->dataSize = 0; - pMP3->atEnd = DRMP3_FALSE; + /* Seek to the start of the stream to begin with. */ + if (!pMP3->onSeek(pMP3->pUserData, 0, drmp3_seek_origin_start)) + return DRMP3_FALSE; - /* TODO: Optimize. - * - * This is inefficient. We simply read frames from the start of the stream. */ - drmp3_uint64 framesRead = drmp3_read_f32(pMP3, frameIndex, NULL); - if (framesRead != frameIndex) - return DRMP3_FALSE; + /* Clear any cached data. */ + pMP3->framesConsumed = 0; + pMP3->framesRemaining = 0; + pMP3->dataSize = 0; + pMP3->atEnd = DRMP3_FALSE; - return DRMP3_TRUE; + /* TODO: Optimize. + * + * This is inefficient. We simply read frames from the start of the stream. */ + framesRead = drmp3_read_f32(pMP3, frameIndex, NULL); + if (framesRead != frameIndex) + return DRMP3_FALSE; + + return DRMP3_TRUE; } float* drmp3__full_decode_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) { - drmp3_assert(pMP3 != NULL); - drmp3_uint64 totalFramesRead = 0; drmp3_uint64 framesCapacity = 0; float* pFrames = NULL; float temp[4096]; + + drmp3_assert(pMP3 != NULL); + for (;;) { drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels; @@ -2603,15 +2633,18 @@ float* drmp3__full_decode_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp /* Reallocate the output buffer if there's not enough room. */ if (framesCapacity < totalFramesRead + framesJustRead) { + float* pNewFrames; + drmp3_uint64 newFramesBufferSize; + framesCapacity *= 2; if (framesCapacity < totalFramesRead + framesJustRead) framesCapacity = totalFramesRead + framesJustRead; - drmp3_uint64 newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float); + newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float); if (newFramesBufferSize > SIZE_MAX) break; - float* pNewFrames = (float*)drmp3_realloc(pFrames, (size_t)newFramesBufferSize); + pNewFrames = (float*)drmp3_realloc(pFrames, (size_t)newFramesBufferSize); if (pNewFrames == NULL) { drmp3_free(pFrames); From 238091f18276d907e418b736fe2791f567cd2012 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 14:33:48 +0200 Subject: [PATCH 511/517] (OSX/Android) Add Dr. Flac/Dr. Mp3 support to Android/OSX --- pkg/android/phoenix/jni/Android.mk | 2 +- pkg/apple/RetroArch.xcodeproj/project.pbxproj | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/android/phoenix/jni/Android.mk b/pkg/android/phoenix/jni/Android.mk index bd87b9d0bf..c5a6291e07 100644 --- a/pkg/android/phoenix/jni/Android.mk +++ b/pkg/android/phoenix/jni/Android.mk @@ -67,7 +67,7 @@ else DEFINES += -DHAVE_OPENGLES2 endif -DEFINES += -DRARCH_MOBILE -DHAVE_GRIFFIN -DHAVE_STB_VORBIS -DHAVE_LANGEXTRA -DANDROID -DHAVE_DYNAMIC -DHAVE_OPENGL -DHAVE_OVERLAY -DHAVE_OPENGLES -DGLSL_DEBUG -DHAVE_DYLIB -DHAVE_EGL -DHAVE_GLSL -DHAVE_MENU -DHAVE_RGUI -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DINLINE=inline -DHAVE_THREADS -D__LIBRETRO__ -DHAVE_RSOUND -DHAVE_NETWORKGAMEPAD -DHAVE_NETWORKING -DRARCH_INTERNAL -DHAVE_FILTERS_BUILTIN -DHAVE_MATERIALUI -DHAVE_XMB -DHAVE_SHADERPIPELINE -DHAVE_LIBRETRODB -DHAVE_STB_FONT -DHAVE_IMAGEVIEWER -DHAVE_UPDATE_ASSETS -DHAVE_CC_RESAMPLER -DHAVE_MINIUPNPC -DHAVE_BUILTINMINIUPNPC -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -DHAVE_KEYMAPPER -DHAVE_NETWORKGAMEPAD -DHAVE_FLAC -DHAVE_CHD -DHAVE_RUNAHEAD +DEFINES += -DRARCH_MOBILE -DHAVE_GRIFFIN -DHAVE_STB_VORBIS -DHAVE_LANGEXTRA -DANDROID -DHAVE_DYNAMIC -DHAVE_OPENGL -DHAVE_OVERLAY -DHAVE_OPENGLES -DGLSL_DEBUG -DHAVE_DYLIB -DHAVE_EGL -DHAVE_GLSL -DHAVE_MENU -DHAVE_RGUI -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DINLINE=inline -DHAVE_THREADS -D__LIBRETRO__ -DHAVE_RSOUND -DHAVE_NETWORKGAMEPAD -DHAVE_NETWORKING -DRARCH_INTERNAL -DHAVE_FILTERS_BUILTIN -DHAVE_MATERIALUI -DHAVE_XMB -DHAVE_SHADERPIPELINE -DHAVE_LIBRETRODB -DHAVE_STB_FONT -DHAVE_IMAGEVIEWER -DHAVE_UPDATE_ASSETS -DHAVE_CC_RESAMPLER -DHAVE_MINIUPNPC -DHAVE_BUILTINMINIUPNPC -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -DHAVE_KEYMAPPER -DHAVE_NETWORKGAMEPAD -DHAVE_FLAC -DHAVE_DR_FLAC -DHAVE_DR_MP3 -DHAVE_CHD -DHAVE_RUNAHEAD DEFINES += -DWANT_IFADDRS ifeq ($(HAVE_VULKAN),1) diff --git a/pkg/apple/RetroArch.xcodeproj/project.pbxproj b/pkg/apple/RetroArch.xcodeproj/project.pbxproj index 3b4f0dd4be..73e4e0c3bf 100644 --- a/pkg/apple/RetroArch.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch.xcodeproj/project.pbxproj @@ -500,6 +500,8 @@ "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_FLAC", + "-DHAVE_DR_FLAC", + "-DHAVE_DR_MP3", "-DHAVE_LROUND", "-DFLAC__HAS_OGG=0", "-DHAVE_CHD", @@ -561,6 +563,8 @@ "-DHAVE_RUNAHEAD", "-DHAVE_GRIFFIN", "-DHAVE_FLAC", + "-DHAVE_DR_FLAC", + "-DHAVE_DR_MP3", "-DHAVE_LROUND", "-DFLAC__HAS_OGG=0", "-DHAVE_CHD", From f14735961aad99f9e9241a8239a01e7c60129144 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 15:14:53 +0200 Subject: [PATCH 512/517] (libFLAC) turn into relative path header includes --- deps/libFLAC/bitmath.c | 2 +- deps/libFLAC/bitreader.c | 14 +++++----- deps/libFLAC/cpu.c | 5 ++-- deps/libFLAC/crc.c | 2 +- deps/libFLAC/fixed.c | 10 +++---- deps/libFLAC/float.c | 6 ++-- deps/libFLAC/format.c | 12 ++++---- deps/libFLAC/include/private/bitmath.h | 6 ++-- deps/libFLAC/include/private/bitreader.h | 2 +- deps/libFLAC/include/private/cpu.h | 2 +- deps/libFLAC/include/private/crc.h | 2 +- deps/libFLAC/include/private/fixed.h | 6 ++-- deps/libFLAC/include/private/float.h | 2 +- deps/libFLAC/include/private/format.h | 2 +- deps/libFLAC/include/private/lpc.h | 6 ++-- deps/libFLAC/include/private/md5.h | 2 +- deps/libFLAC/include/private/memory.h | 4 +-- .../include/protected/stream_decoder.h | 4 +-- deps/libFLAC/include/share/alloc.h | 2 +- deps/libFLAC/lpc.c | 12 ++++---- deps/libFLAC/lpc_intrin_avx2.c | 8 +++--- deps/libFLAC/lpc_intrin_sse.c | 8 +++--- deps/libFLAC/lpc_intrin_sse2.c | 8 +++--- deps/libFLAC/lpc_intrin_sse41.c | 8 +++--- deps/libFLAC/md5.c | 6 ++-- deps/libFLAC/memory.c | 6 ++-- deps/libFLAC/stream_decoder.c | 28 +++++++++---------- 27 files changed, 88 insertions(+), 87 deletions(-) diff --git a/deps/libFLAC/bitmath.c b/deps/libFLAC/bitmath.c index b3d797d39b..b700b71e0c 100644 --- a/deps/libFLAC/bitmath.c +++ b/deps/libFLAC/bitmath.c @@ -34,7 +34,7 @@ # include #endif -#include "private/bitmath.h" +#include "include/private/bitmath.h" /* An example of what FLAC__bitmath_silog2() computes: * diff --git a/deps/libFLAC/bitreader.c b/deps/libFLAC/bitreader.c index 902c3a3327..5efde25e3f 100644 --- a/deps/libFLAC/bitreader.c +++ b/deps/libFLAC/bitreader.c @@ -38,13 +38,13 @@ #include #include -#include "private/bitmath.h" -#include "private/bitreader.h" -#include "private/crc.h" -#include "private/macros.h" -#include "FLAC/assert.h" -#include "share/compat.h" -#include "share/endswap.h" +#include "include/private/bitmath.h" +#include "include/private/bitreader.h" +#include "include/private/crc.h" +#include "include/private/macros.h" +#include "include/FLAC/assert.h" +#include "include/share/compat.h" +#include "include/share/endswap.h" /* Things should be fastest when this matches the machine word size */ /* WATCHOUT: if you change this you must also change the following #defines down to COUNT_ZERO_MSBS2 below to match */ diff --git a/deps/libFLAC/cpu.c b/deps/libFLAC/cpu.c index dc25863641..da2b7e3955 100644 --- a/deps/libFLAC/cpu.c +++ b/deps/libFLAC/cpu.c @@ -34,11 +34,12 @@ # include #endif -#include "private/cpu.h" -#include "share/compat.h" #include #include +#include "include/private/cpu.h" +#include "include/share/compat.h" + #if defined(_MSC_VER) # include /* for __cpuid() and _xgetbv() */ #endif diff --git a/deps/libFLAC/crc.c b/deps/libFLAC/crc.c index 8123c3b69d..b1068cedad 100644 --- a/deps/libFLAC/crc.c +++ b/deps/libFLAC/crc.c @@ -34,7 +34,7 @@ # include #endif -#include "private/crc.h" +#include "include/private/crc.h" /* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ diff --git a/deps/libFLAC/fixed.c b/deps/libFLAC/fixed.c index b33f61f716..f444a1e2dc 100644 --- a/deps/libFLAC/fixed.c +++ b/deps/libFLAC/fixed.c @@ -38,11 +38,11 @@ #include #include -#include "share/compat.h" -#include "private/bitmath.h" -#include "private/fixed.h" -#include "private/macros.h" -#include "FLAC/assert.h" +#include "include/share/compat.h" +#include "include/private/bitmath.h" +#include "include/private/fixed.h" +#include "include/private/macros.h" +#include "include/FLAC/assert.h" #ifdef local_abs #undef local_abs diff --git a/deps/libFLAC/float.c b/deps/libFLAC/float.c index 25d1a78615..fdcfcb0a91 100644 --- a/deps/libFLAC/float.c +++ b/deps/libFLAC/float.c @@ -34,9 +34,9 @@ # include #endif -#include "FLAC/assert.h" -#include "share/compat.h" -#include "private/float.h" +#include "include/FLAC/assert.h" +#include "include/share/compat.h" +#include "include/private/float.h" #ifdef FLAC__INTEGER_ONLY_LIBRARY diff --git a/deps/libFLAC/format.c b/deps/libFLAC/format.c index f320807404..655a908eb9 100644 --- a/deps/libFLAC/format.c +++ b/deps/libFLAC/format.c @@ -40,12 +40,12 @@ #include -#include "FLAC/assert.h" -#include "FLAC/format.h" -#include "share/alloc.h" -#include "share/compat.h" -#include "private/format.h" -#include "private/macros.h" +#include "include/FLAC/assert.h" +#include "include/FLAC/format.h" +#include "include/share/alloc.h" +#include "include/share/compat.h" +#include "include/private/format.h" +#include "include/private/macros.h" /* FLAC_PACKAGE_VERSION should come from configure */ #if defined(__LIBRETRO__) || defined(RARCH_INTERNAL) diff --git a/deps/libFLAC/include/private/bitmath.h b/deps/libFLAC/include/private/bitmath.h index 2c8ecf53b9..24ba5e6003 100644 --- a/deps/libFLAC/include/private/bitmath.h +++ b/deps/libFLAC/include/private/bitmath.h @@ -35,10 +35,10 @@ #include -#include "FLAC/ordinals.h" -#include "FLAC/assert.h" +#include "../FLAC/ordinals.h" +#include "../FLAC/assert.h" -#include "share/compat.h" +#include "../share/compat.h" #if defined(_MSC_VER) #include /* for _BitScanReverse* */ diff --git a/deps/libFLAC/include/private/bitreader.h b/deps/libFLAC/include/private/bitreader.h index 7c7316556a..91230eee8c 100644 --- a/deps/libFLAC/include/private/bitreader.h +++ b/deps/libFLAC/include/private/bitreader.h @@ -34,7 +34,7 @@ #define FLAC__PRIVATE__BITREADER_H #include /* for FILE */ -#include "FLAC/ordinals.h" +#include "../FLAC/ordinals.h" #include "cpu.h" /* diff --git a/deps/libFLAC/include/private/cpu.h b/deps/libFLAC/include/private/cpu.h index 7c6518076b..1353817fe8 100644 --- a/deps/libFLAC/include/private/cpu.h +++ b/deps/libFLAC/include/private/cpu.h @@ -33,7 +33,7 @@ #ifndef FLAC__PRIVATE__CPU_H #define FLAC__PRIVATE__CPU_H -#include "FLAC/ordinals.h" +#include "../FLAC/ordinals.h" #ifdef HAVE_CONFIG_H #include diff --git a/deps/libFLAC/include/private/crc.h b/deps/libFLAC/include/private/crc.h index 294f60eaa2..6c63b3b272 100644 --- a/deps/libFLAC/include/private/crc.h +++ b/deps/libFLAC/include/private/crc.h @@ -33,7 +33,7 @@ #ifndef FLAC__PRIVATE__CRC_H #define FLAC__PRIVATE__CRC_H -#include "FLAC/ordinals.h" +#include "../FLAC/ordinals.h" /* 8 bit CRC generator, MSB shifted first ** polynomial = x^8 + x^2 + x^1 + x^0 diff --git a/deps/libFLAC/include/private/fixed.h b/deps/libFLAC/include/private/fixed.h index 68cdfceb37..b260d29697 100644 --- a/deps/libFLAC/include/private/fixed.h +++ b/deps/libFLAC/include/private/fixed.h @@ -37,9 +37,9 @@ #include #endif -#include "private/cpu.h" -#include "private/float.h" -#include "FLAC/format.h" +#include "../private/cpu.h" +#include "../private/float.h" +#include "../FLAC/format.h" /* * FLAC__fixed_compute_best_predictor() diff --git a/deps/libFLAC/include/private/float.h b/deps/libFLAC/include/private/float.h index 12ece60565..7d50f6103e 100644 --- a/deps/libFLAC/include/private/float.h +++ b/deps/libFLAC/include/private/float.h @@ -37,7 +37,7 @@ #include #endif -#include "FLAC/ordinals.h" +#include "../FLAC/ordinals.h" /* * All the code in libFLAC that uses float and double diff --git a/deps/libFLAC/include/private/format.h b/deps/libFLAC/include/private/format.h index 5b9cfbd0ed..e27f251f85 100644 --- a/deps/libFLAC/include/private/format.h +++ b/deps/libFLAC/include/private/format.h @@ -33,7 +33,7 @@ #ifndef FLAC__PRIVATE__FORMAT_H #define FLAC__PRIVATE__FORMAT_H -#include "FLAC/format.h" +#include "../FLAC/format.h" unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order); unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned blocksize); diff --git a/deps/libFLAC/include/private/lpc.h b/deps/libFLAC/include/private/lpc.h index 6eb02be63a..bf7b5d7f25 100644 --- a/deps/libFLAC/include/private/lpc.h +++ b/deps/libFLAC/include/private/lpc.h @@ -37,9 +37,9 @@ #include #endif -#include "private/cpu.h" -#include "private/float.h" -#include "FLAC/format.h" +#include "../private/cpu.h" +#include "../private/float.h" +#include "../FLAC/format.h" #ifndef FLAC__INTEGER_ONLY_LIBRARY diff --git a/deps/libFLAC/include/private/md5.h b/deps/libFLAC/include/private/md5.h index c665ab313f..3d4967c091 100644 --- a/deps/libFLAC/include/private/md5.h +++ b/deps/libFLAC/include/private/md5.h @@ -26,7 +26,7 @@ * Still in the public domain, with no warranty. */ -#include "FLAC/ordinals.h" +#include "../FLAC/ordinals.h" typedef union { FLAC__byte *p8; diff --git a/deps/libFLAC/include/private/memory.h b/deps/libFLAC/include/private/memory.h index f103c531ff..d9116e8bb6 100644 --- a/deps/libFLAC/include/private/memory.h +++ b/deps/libFLAC/include/private/memory.h @@ -39,8 +39,8 @@ #include /* for size_t */ -#include "private/float.h" -#include "FLAC/ordinals.h" /* for FLAC__bool */ +#include "../private/float.h" +#include "../FLAC/ordinals.h" /* for FLAC__bool */ /* Returns the unaligned address returned by malloc. * Use free() on this address to deallocate. diff --git a/deps/libFLAC/include/protected/stream_decoder.h b/deps/libFLAC/include/protected/stream_decoder.h index 5c31c1618d..bf08aa6081 100644 --- a/deps/libFLAC/include/protected/stream_decoder.h +++ b/deps/libFLAC/include/protected/stream_decoder.h @@ -33,9 +33,9 @@ #ifndef FLAC__PROTECTED__STREAM_DECODER_H #define FLAC__PROTECTED__STREAM_DECODER_H -#include "FLAC/stream_decoder.h" +#include "../FLAC/stream_decoder.h" #if FLAC__HAS_OGG -#include "private/ogg_decoder_aspect.h" +#include "../private/ogg_decoder_aspect.h" #endif typedef struct FLAC__StreamDecoderProtected { diff --git a/deps/libFLAC/include/share/alloc.h b/deps/libFLAC/include/share/alloc.h index 19349d2a75..a66153eee9 100644 --- a/deps/libFLAC/include/share/alloc.h +++ b/deps/libFLAC/include/share/alloc.h @@ -48,7 +48,7 @@ #include /* for SIZE_MAX in case limits.h didn't get it */ #endif #include /* for size_t, malloc(), etc */ -#include "share/compat.h" +#include "../share/compat.h" #ifndef SIZE_MAX # ifndef SIZE_T_MAX diff --git a/deps/libFLAC/lpc.c b/deps/libFLAC/lpc.c index 531247b595..a6ae94446d 100644 --- a/deps/libFLAC/lpc.c +++ b/deps/libFLAC/lpc.c @@ -36,12 +36,12 @@ #include -#include "FLAC/assert.h" -#include "FLAC/format.h" -#include "share/compat.h" -#include "private/bitmath.h" -#include "private/lpc.h" -#include "private/macros.h" +#include "include/FLAC/assert.h" +#include "include/FLAC/format.h" +#include "include/share/compat.h" +#include "include/private/bitmath.h" +#include "include/private/lpc.h" +#include "include/private/macros.h" #if defined DEBUG || defined FLAC__OVERFLOW_DETECT || defined FLAC__OVERFLOW_DETECT_VERBOSE #include #endif diff --git a/deps/libFLAC/lpc_intrin_avx2.c b/deps/libFLAC/lpc_intrin_avx2.c index f9f5ccdb02..44e2169025 100644 --- a/deps/libFLAC/lpc_intrin_avx2.c +++ b/deps/libFLAC/lpc_intrin_avx2.c @@ -34,16 +34,16 @@ # include #endif -#include "private/cpu.h" +#include "include/private/cpu.h" #ifndef FLAC__INTEGER_ONLY_LIBRARY #ifndef FLAC__NO_ASM #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN -#include "private/lpc.h" +#include "include/private/lpc.h" #ifdef FLAC__AVX2_SUPPORTED -#include "FLAC/assert.h" -#include "FLAC/format.h" +#include "include/FLAC/assert.h" +#include "include/FLAC/format.h" #include /* AVX2 */ diff --git a/deps/libFLAC/lpc_intrin_sse.c b/deps/libFLAC/lpc_intrin_sse.c index 430e73f08e..9c800f5831 100644 --- a/deps/libFLAC/lpc_intrin_sse.c +++ b/deps/libFLAC/lpc_intrin_sse.c @@ -34,15 +34,15 @@ # include #endif -#include "private/cpu.h" +#include "include/private/cpu.h" #ifndef FLAC__INTEGER_ONLY_LIBRARY #ifndef FLAC__NO_ASM #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN -#include "private/lpc.h" +#include "include/private/lpc.h" #ifdef FLAC__SSE_SUPPORTED -#include "FLAC/assert.h" -#include "FLAC/format.h" +#include "include/FLAC/assert.h" +#include "include/FLAC/format.h" #include /* SSE */ diff --git a/deps/libFLAC/lpc_intrin_sse2.c b/deps/libFLAC/lpc_intrin_sse2.c index 138339483f..59b416253f 100644 --- a/deps/libFLAC/lpc_intrin_sse2.c +++ b/deps/libFLAC/lpc_intrin_sse2.c @@ -34,16 +34,16 @@ # include #endif -#include "private/cpu.h" +#include "include/private/cpu.h" #ifndef FLAC__INTEGER_ONLY_LIBRARY #ifndef FLAC__NO_ASM #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN -#include "private/lpc.h" +#include "include/private/lpc.h" #ifdef FLAC__SSE2_SUPPORTED -#include "FLAC/assert.h" -#include "FLAC/format.h" +#include "include/FLAC/assert.h" +#include "include/FLAC/format.h" #include /* SSE2 */ diff --git a/deps/libFLAC/lpc_intrin_sse41.c b/deps/libFLAC/lpc_intrin_sse41.c index bef73f41f6..ea0e2bbc30 100644 --- a/deps/libFLAC/lpc_intrin_sse41.c +++ b/deps/libFLAC/lpc_intrin_sse41.c @@ -34,16 +34,16 @@ # include #endif -#include "private/cpu.h" +#include "include/private/cpu.h" #ifndef FLAC__INTEGER_ONLY_LIBRARY #ifndef FLAC__NO_ASM #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN -#include "private/lpc.h" +#include "include/private/lpc.h" #ifdef FLAC__SSE4_1_SUPPORTED -#include "FLAC/assert.h" -#include "FLAC/format.h" +#include "include/FLAC/assert.h" +#include "include/FLAC/format.h" #include /* SSE4.1 */ diff --git a/deps/libFLAC/md5.c b/deps/libFLAC/md5.c index 9527f38deb..7ca816133e 100644 --- a/deps/libFLAC/md5.c +++ b/deps/libFLAC/md5.c @@ -5,9 +5,9 @@ #include /* for malloc() */ #include /* for memcpy() */ -#include "private/md5.h" -#include "share/alloc.h" -#include "share/endswap.h" +#include "include/private/md5.h" +#include "include/share/alloc.h" +#include "include/share/endswap.h" /* * This code implements the MD5 message-digest algorithm. diff --git a/deps/libFLAC/memory.c b/deps/libFLAC/memory.c index 41cc446034..a7a3f6a721 100644 --- a/deps/libFLAC/memory.c +++ b/deps/libFLAC/memory.c @@ -38,9 +38,9 @@ #include #endif -#include "private/memory.h" -#include "FLAC/assert.h" -#include "share/alloc.h" +#include "include/private/memory.h" +#include "include/FLAC/assert.h" +#include "include/share/alloc.h" void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) { diff --git a/deps/libFLAC/stream_decoder.c b/deps/libFLAC/stream_decoder.c index 8dab5caa18..ec34f75667 100644 --- a/deps/libFLAC/stream_decoder.c +++ b/deps/libFLAC/stream_decoder.c @@ -42,20 +42,20 @@ #include -#include "share/compat.h" -#include "FLAC/assert.h" -#include "share/alloc.h" -#include "protected/stream_decoder.h" -#include "private/bitreader.h" -#include "private/bitmath.h" -#include "private/cpu.h" -#include "private/crc.h" -#include "private/fixed.h" -#include "private/format.h" -#include "private/lpc.h" -#include "private/md5.h" -#include "private/memory.h" -#include "private/macros.h" +#include "include/share/compat.h" +#include "include/FLAC/assert.h" +#include "include/share/alloc.h" +#include "include/protected/stream_decoder.h" +#include "include/private/bitreader.h" +#include "include/private/bitmath.h" +#include "include/private/cpu.h" +#include "include/private/crc.h" +#include "include/private/fixed.h" +#include "include/private/format.h" +#include "include/private/lpc.h" +#include "include/private/md5.h" +#include "include/private/memory.h" +#include "include/private/macros.h" /* technically this should be in an "export.c" but this is convenient enough */ From 5e1a698fa25bbe346ee866f4210a86533b175263 Mon Sep 17 00:00:00 2001 From: radius Date: Sat, 28 Apr 2018 11:42:48 -0500 Subject: [PATCH 513/517] fix #6644 --- input/input_mapper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index 253ffcf112..6794fb85cb 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -191,7 +191,7 @@ void input_mapper_poll(input_mapper_t *handle) settings->uints.input_remap_ids[i][k]; if ( - (current_axis_value != 0) && + (abs(current_axis_value) > *input_driver_get_float(INPUT_ACTION_AXIS_THRESHOLD)) && (k != remap_axis) && (remap_axis != RARCH_UNMAPPED) ) From 7b82c8a9bc56a5eca4bff4e7abbfd92fa0adae51 Mon Sep 17 00:00:00 2001 From: radius Date: Sat, 28 Apr 2018 11:55:59 -0500 Subject: [PATCH 514/517] fix #6644 --- input/input_mapper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/input/input_mapper.c b/input/input_mapper.c index 6794fb85cb..e4140765ee 100644 --- a/input/input_mapper.c +++ b/input/input_mapper.c @@ -191,7 +191,8 @@ void input_mapper_poll(input_mapper_t *handle) settings->uints.input_remap_ids[i][k]; if ( - (abs(current_axis_value) > *input_driver_get_float(INPUT_ACTION_AXIS_THRESHOLD)) && + (abs(current_axis_value) > + *input_driver_get_float(INPUT_ACTION_AXIS_THRESHOLD) * 32767) && (k != remap_axis) && (remap_axis != RARCH_UNMAPPED) ) From 5a80a6046f0ae41c5130c59ea52e8a7b6526a9b8 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 19:29:07 +0200 Subject: [PATCH 515/517] (Android) Remove no longer needed machine/cpu-features.h --- frontend/drivers/platform_unix.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/drivers/platform_unix.c b/frontend/drivers/platform_unix.c index 338200cc0d..11e6c2c8d8 100644 --- a/frontend/drivers/platform_unix.c +++ b/frontend/drivers/platform_unix.c @@ -54,9 +54,6 @@ #ifdef ANDROID #include -#ifdef __arm__ -#include -#endif #endif #include From b6df41d8084fcf699f7e51620d3f8bb5e65cbf46 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 20:06:07 +0200 Subject: [PATCH 516/517] (audio_driver.c) Cleanups --- audio/audio_driver.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 7db29ef441..3209354b19 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -524,7 +524,7 @@ static bool audio_driver_init_internal(bool audio_cb_inited) { audio_driver_buffer_size = current_audio->buffer_size(audio_driver_context_audio_data); - audio_driver_control = true; + audio_driver_control = true; } else RARCH_WARN("Audio rate control was desired, but driver does not support needed features.\n"); @@ -576,20 +576,17 @@ void audio_driver_set_nonblocking_state(bool enable) static void audio_driver_flush(const int16_t *data, size_t samples) { struct resampler_data src_data; - bool is_perfcnt_enable = false; - bool is_paused = false; - bool is_idle = false; - bool is_slowmotion = false; - const void *output_data = NULL; - unsigned output_frames = 0; - float audio_volume_gain = !audio_driver_mute_enable ? + bool is_perfcnt_enable = false; + bool is_paused = false; + bool is_idle = false; + bool is_slowmotion = false; + const void *output_data = NULL; + unsigned output_frames = 0; + float audio_volume_gain = !audio_driver_mute_enable ? audio_driver_volume_gain : 0.0f; - src_data.data_in = NULL; - src_data.data_out = NULL; - src_data.input_frames = 0; - src_data.output_frames = 0; - src_data.ratio = 0.0f; + src_data.data_out = NULL; + src_data.output_frames = 0; if (recording_data) recording_push_audio(data, samples); @@ -606,8 +603,8 @@ static void audio_driver_flush(const int16_t *data, size_t samples) convert_s16_to_float(audio_driver_input_data, data, samples, audio_volume_gain); - src_data.data_in = audio_driver_input_data; - src_data.input_frames = samples >> 1; + src_data.data_in = audio_driver_input_data; + src_data.input_frames = samples >> 1; if (audio_driver_dsp) @@ -662,7 +659,7 @@ static void audio_driver_flush(const int16_t *data, size_t samples) #endif } - src_data.ratio = audio_source_ratio_current; + src_data.ratio = audio_source_ratio_current; if (is_slowmotion) { From 686faa240711fdf0f88f1a958157102f0633e3de Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sat, 28 Apr 2018 21:40:36 +0200 Subject: [PATCH 517/517] Add experimental-audio.diff --- experimental-audio.diff | 97 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 experimental-audio.diff diff --git a/experimental-audio.diff b/experimental-audio.diff new file mode 100644 index 0000000000..7697070fb2 --- /dev/null +++ b/experimental-audio.diff @@ -0,0 +1,97 @@ +diff --git a/audio/audio_driver.c b/audio/audio_driver.c +index 3209354b19..caf10dbed2 100644 +--- a/audio/audio_driver.c ++++ b/audio/audio_driver.c +@@ -606,7 +606,6 @@ static void audio_driver_flush(const int16_t *data, size_t samples) + src_data.data_in = audio_driver_input_data; + src_data.input_frames = samples >> 1; + +- + if (audio_driver_dsp) + { + struct retro_dsp_data dsp_data; +@@ -722,6 +721,19 @@ void audio_driver_sample(int16_t left, int16_t right) + audio_driver_data_ptr = 0; + } + ++void audio_driver_menu_sample(void) ++{ ++ static unsigned count = 0; ++ audio_driver_output_samples_conv_buf[count++] = 0; ++ audio_driver_output_samples_conv_buf[count++] = 0; ++ ++ if (audio_driver_data_ptr < audio_driver_chunk_size) ++ return; ++ ++ audio_driver_flush(audio_driver_output_samples_conv_buf, ++ count); ++} ++ + /** + * audio_driver_sample_batch: + * @data : pointer to audio buffer. +diff --git a/audio/audio_driver.h b/audio/audio_driver.h +index 6dfacaa89f..e5c37a344c 100644 +--- a/audio/audio_driver.h ++++ b/audio/audio_driver.h +@@ -271,6 +271,8 @@ bool audio_driver_deinit(void); + + bool audio_driver_init(void); + ++void audio_driver_menu_sample(void); ++ + bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params); + + enum resampler_quality audio_driver_get_resampler_quality(void); +diff --git a/menu/menu_driver.c b/menu/menu_driver.c +index 45580c12f5..894cbf56bc 100644 +--- a/menu/menu_driver.c ++++ b/menu/menu_driver.c +@@ -1683,8 +1683,10 @@ static void menu_driver_toggle(bool on) + /* Stop all rumbling before entering the menu. */ + command_event(CMD_EVENT_RUMBLE_STOP, NULL); + ++#if 0 + if (pause_libretro) + command_event(CMD_EVENT_AUDIO_STOP, NULL); ++#endif + + /* Override keyboard callback to redirect to menu instead. + * We'll use this later for something ... */ +@@ -1702,8 +1704,10 @@ static void menu_driver_toggle(bool on) + if (!rarch_ctl(RARCH_CTL_IS_SHUTDOWN, NULL)) + driver_set_nonblock_state(); + ++#if 0 + if (pause_libretro) + command_event(CMD_EVENT_AUDIO_START, NULL); ++#endif + + /* Restore libretro keyboard callback. */ + if (key_event && frontend_key_event) +diff --git a/retroarch.c b/retroarch.c +index 537ed82f37..29df4434b0 100644 +--- a/retroarch.c ++++ b/retroarch.c +@@ -2608,6 +2608,7 @@ static enum runloop_state runloop_check_state( + + retro_ctx.poll_cb(); + ++ + { + enum menu_action action; + bool focused = false; +@@ -2627,10 +2628,13 @@ static enum runloop_state runloop_check_state( + rarch_menu_running_finished(); + + if (focused || !runloop_idle) ++ { + menu_driver_render(runloop_idle, rarch_is_inited, + (current_core_type == CORE_TYPE_DUMMY) + ) + ; ++ audio_driver_menu_sample(); ++ } + + old_input = current_input; +

    N~;FmV2P?Y98CWZjI4k?)1M^<1Ni^FpgnwNeVF z>0*0;>F-Ypda2WK*7N+Ii=N%1gXm@eaJrLp+cVfq(lLs&7|QtU`|k&fhJpev;7zOJ z`VN1}8rCSq<2^xdBS8`ry;_im9Brnj;~A3po<)OHp-fTnzgj?ujvfnoi!a|?V`kTJxDta1bvZa5 zb^(z1j-DC)__0{JKRNDbmhXii4mwyQK)h10liNNgM`jQ8J0YljSn^Jp zMJ>y=yu6%+K`wFxo9Iz#xqe-=O16aIRIS}d*9D(5tS2Z1d;-XAXHwds?Z(v$P|}9g zc%^!e77QH1ScL>;VQ}gSz%_h&PfEIgAlkgo znIRP<1%6fL3@rT@XV4M-w=Q`%B?k)#3`AFD3kEmfz3gh-*f!Wp|LvzYAT=}ocAai= z-2AJ@Z*oW+xJTh~vCBtNTLHgKG3`ym|HdR~j3wU{Uwj?@39RkQ6s|z_m9)ptStuS)1MI6aC46 zKH9TwUER(xSZNg$#ws<+y>xSC_wECEU*)vk)+7F(G@1s~GB>^@uDe^G-ES{cY|rey zwlvjFs*{qKJg!b=j4lqkJ`m)RkE?ySi2>%%t|eF+Hc0P)u7>p~L%;}KZ}p3w+l?34 z7XLE{6NX|0b-X6+!zy_Biq9Y3l!hJ?mI2SBiYnF7){c*&68sq;C8esM5bm*_>Gm2Z z{O+vn z?#a!rgWpl;5eKc$aa{G=KP9nMf?zvaTeE#BF3t%L4>tuDW@P4!F8fk>cPp6O=-?Rm z6v9u*Oj=TdkLlkQrSV$!0?{rsF?^`qsRJwNG>P5NdbH^5 zQ+h81nQE60J?{>V9qQ9=1N-w?(!Kl5#tQf6SGXf;Wv&E=l%9a8j zTRPc1AsG3%b!oK%PmCPth?KXwsrvQ36kwXY&d$!9T*`^Pxh6r6P89C9o6D0R;B5!C z5)~481GhOBZogY^DOn0WD>xlJaE89J{j_pQ(NxE2)J*Z1oqg%{YTqGjB&+%%W;>b` zm7smbDZuzYJ_^{yFLq? z7#&)SU@tp31M7_2A*;vrK|DIHT2ob3)$!ur-}fM2pWoLpAa0D|=m+Upjn=5nKGXL2 zVTA!LB*lw)ZnZx-wp#yA^+Vq@oquem*-XnvbCoP=J)5lK-%6(#!xCQgY2|omP-3>P z>E>u5le`fZf$LtWme|Fc;A#ijxGyDdAph5{wRI*tlQJ|#%525A{l?=35{I&WkiXQ~ zo_V3i>r^y^-L!L=;A76Ph`gNPb&Mflz3q_)xYY{(0SYiT@PHPoH{dKSFUJABBaIt^ zP}E|Ez8(2!+0ujD31W3Y%CzyZvF{%R9cs07bY?&ezn?uE`X4mR@K{S*JiC$}<%?y+ z?TwG?3f*{a+(Ng?8J^L*= zE$uVN>-u0|W(_2XYpdb+`z>dWvbaqG1!r-|Apg!zq$EN`Fy88Ui}F{b>~B?2hU6fB zFqy)zmk<|+2C6eb^>X5P=eLDmc;R&U$KlD}-^O1rGz-7E(@{y&=o58@v#E(EeW#=> zHtQo4-a8sLA1+Ao{CdJrQ^U7%dzFwnq0}(SMtJ|emSd>Qp!-^yDk3mbtRdq?361w* z&mWpB@26Che=OCH)+NiiSDE>8(lMSCyyVlP=XgT`Vr9&QCjDcW&( awU45A_T^0 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 diff --git a/pkg/ctr/assets/nxengine.png b/pkg/ctr/assets/nxengine.png index d6c788a77904f9c88531b33c065c62b1e0500d9d..f5077b0aba02c2c24ff2640f84588e2521a1df6d 100644 GIT binary patch delta 682 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 Pr^9O{O~o(r){*}oCBv73 literal 26236 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 diff --git a/pkg/ctr/assets/o2em.png b/pkg/ctr/assets/o2em.png index 5be6e266b4d2e421cb36572dd51e3e31bb2835d9..db0595c3d325c22057116d727b852a2389daad5f 100644 GIT binary patch delta 322 zcmV-I0log$0^I_TB!6H~OjJcEC@5bEq)CLCo0093001^@sJ0&H#3k&H10T>t< zARr*W008~~0Mq~g*#H3V007Sb0P+9;(f|O-007GX0M`Hj+W-Lf007ni0Qdj^=G{5=xbosOp9goRlXS3-UxFkrhY5WJ?Npm$AABW&*3h^IFY>V>Jv# z(Ppd-))7=IsXGX~Vs)PKY9`ETP?gvf$S(bM=~qKYG9DBnJy#2(mTh4SwS0o`54|fm U>MJn8UH||907*qoM6N<$g6(~F^8f$< delta 316 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 OT}w?*wef-d^Zx-0=<3n{ literal 14771 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 MuPj|IX%hVZ0Fz8IApigX diff --git a/pkg/ctr/assets/pcsx_rearmed.png b/pkg/ctr/assets/pcsx_rearmed.png index a7a57d55a59d58ebc71cb41337bcf05c9ef63b55..d06c555ea5a4e8e188839a00ec2875428688e308 100644 GIT binary patch delta 587 zcmV-R0<`_(57`8e8Gi!+003az3AF$K09{Z_R7FQ{Osb@&udA%Ft*)=1pRA^)o0622 zh>N0}ou{Lxmz0)iXKGGOQA9;aac^-$LPjTmegW0WsHCL{#JU)voJLYwnU$9W&BIMY zN1swq*jrcjh}MO%=2c*EVs?@DPWOiZ000VfQchCQf0ij7m zK~#7FrIrVx>L3tBBcxI?_OAP1<{!p-#|Hl$yWT^`P~Dgq`vo(pkh*#N)^-otdofLY zjrPJ*@$J(G)3=ZJ!(D*9?f8T9AqMZ#C#wye=ZG2U!e~T51M?zCekz6tXsA33PVzG` z90=CbEX=JEJbwq{V!DX@>QS&wRn4syLaJa%%Tz^Bf}K?6MTn__6|ML|Q46NB?`q)E zr50TS^G5Joc%(};K1W#7a<)m#mgK$)Fb~3LGX$9FJVCD|6x4cVPzPF*gtmwEi9mIx zZO`o21;??mO2l|2upXF(f!rZLZgHuQBG^DJmXp9C<^f->}31M&|+#_B8c9t1YNAAeF_ Z_y;bBK8!F}pO*jt002ovPDHLkV1k187?}V7 delta 2016 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 J`ufBo=zmt&QUm}1 literal 17619 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 IM6N<$f=4TI`v3p{ delta 766 zcmVMzCV_|S*E^l&Yo9;Xs0007rNklIQn1`}FjO)jjki+mC?l1GonVFI(M1PqP0AwA=Xb@bmfdNbCU)@*lf`e;B^ime~up znF;(OK&#Pgi+|DV%hy~+t8ZR)#O+r9=OxsvgZygCy4jiK)!*E1<+A%!!hLge6kqrE zq51s)fKs`PCyyTqfxmtC{(QL~OyDBi0}WNHHLT_GC=~YavseTG1cL#D$Hx$lMd9@- z9h-|_as}Aj+D0m!fh-fKs)~`}s{jB>>)6=%glsmCV`({! zP-qB5l3Mo3GJz};7zV}CatZ*rLWpEC?}2KyhEzJ!)~H#8NC;gSLTV+0TCK)xUNn;# zU@e!2EEA?tvmqf8vbl9;ooFUAz)oQos!ugFYRIRm_)^$q)`(^@1C&Z-NFEQjnIw5o zDwdcvqJNpp04{Eu=7ZQriDohbXqpCEuXFoQT8F0jnKh!B%mCr=81#zHZC25BOoYdo zHKLi!0P$E9XJ-ZsgPIx{1_h-Q@tG*IPBfDl!0T0zTuk8T_=IU_7!*gxCrCa`Kp7Zd zHYJ*A`X+9?1yd^-5F){+s*t2VPZ3J%sOUNE0o=WF8}XT_>6z~rlY2I*C<+$l=ddt8*HNSWX4?LH!1kY%wjTlK w@3hsjeE{e0wAFF~SS=@jBS(%LIkL5W13Hs3vySesiU0rr07*qoM6N<$f{{F1>;M1& diff --git a/pkg/ctr/assets/picodrive_banner.png b/pkg/ctr/assets/picodrive_banner.png index 54016110573ab545358fe6e199c43c0c4de611ff..770107e624e6b4660875c8f4d6ee5767cceff835 100644 GIT binary patch literal 7324 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 literal 18300 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 delta 1331 zcmV-31}c zS2YN@szJb24Faxek|Y7HYAB_^RSg2JY7p>KGtYBf*TA;MG=EL{D9cjQGTR+xEN5J{5)`eQeu?e^%FZuIm!SBuJL!X`1K_r{(+p z&uz~_Q50BHReu$p_-@z-<#~>mg`>hJMG*6#VHldGAur}ZK@d2O!%@(`_RB#@k|Yek zJ+^1swr!qm+jxbu?@`Wvjl&|e2*=tszp1>-?WnA+Ifo`=rk!7WzcQ-CS{GUu`hSH| z0sqHY_m5@gwz_p&b@{u@(g)Gyq~H4&`2ywpIO_qmy?<`oT3OOiOQ8OO&|`7m)}H^^ zt-)ir29Dk0KiqX5Y%K39Ol?SxJsh#Qr&OKH>`)~odqc{5wwQT!})4P(xzeI+jZl}45M4h-Zdgh`0xyP&L z9;=*rC`Wi2%1V-Ur$+QBA>?+u<$L;uG7$}ki_di~J>SELw|scGW8Yty+npNT8MD0Y zpLII*PH@J@$0ZFEnLc%2n!qkS-@W*3=iC$3Q-Aj)HI!q2d2V-lL`7NH-+w3cQ`xBr zX_V)>7oY12A7?%=#*#s}*3|bVkz+=V^jtLc-uxt;s8NQwGVcem6x5=I z3Q;CdWE~sGhr14ef}^7JLL3r@npS3t;EvxOLWLI~4%U~n^b8BixAB>1Gzy430b;2_ zpqOxGij<%QjTZ!Vu(<+?1~NqHg*dWklz#*DMY2kz54COV$7XdXGNS^)-=`^iGMO}W z0>v(YveAG1fg(5X4N+XLfl|ia@7<6X-IE>1034fnL3(9C{B2B(wF(z9vRx1{ZIwyt$Cw~J} zVi3rl%&1T?L6Ikf3>F6{N0x3ge;eYXuapukZB?sP0ZJqiiql^}HJiBP+A#Pa!kI@IUEvFo8&5@mMzLBuwj zjfi9n?3F!-#fty{002ovPDHLkV1g3mhL`{V diff --git a/pkg/ctr/assets/prosystem_banner.png b/pkg/ctr/assets/prosystem_banner.png index a3c616585dbc90a923508b1bfef177a614af62c8..87308ad553db56d1d29c52594c5dbbe0441295f1 100644 GIT binary patch literal 6883 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 literal 15952 zcmYLwWmFtZu=Xr0?iSo#5+FcuSb_%vgb+NqOYq>ZK!R(4puyeUWeFPG-QC@7Ki==2 zb8r2aGpA>IrmDK8x}T?}LsXPxvCzrT0RX^~my=Qj0MN@R07XT9Id0b0zg!R_mY0%H za|0btb`$w?;{t{hO-kZAkTOe}UfokA`>2g~E1m(u@NXMb|MmP|8NH;-(kDIKa=dE@ z?$g27e|P$!aN7M~A-HcKJ+>(Klxa@lradvZW<+P@l}Y#>?vujv87G}Y<<{x3sS1UM zZ(l3tQswk_rx3ZQU1E%=n~J_Hxi>#F-uNG`Qj4wL!P0$McXUspr<=sdA|gwvh5Pu@ zN7}iwh=k2L$UYK)*KnemU&PbL`Lv;V?U(-f)I}9iXJwZt;^fh{k)TIB zhz*c!2HtR9Nw-1N)vL}moDQ5!96wh_WOq9|PI61-$lA)v7*7WWJff?7sihRqfY+>u z4Q3{-$Tu*x0fOafw&JNE4GP~W?V*zPfxED<@ZIk7vlx7JH3K%@_P3y*orYT#cUp`C z2KEa6BcfT!I*a?B2g=W#X!u|MC>QU5C`>UFtW5q>g?=P zRe0#n%JRw9-FQ`tgLqoe+WHlfJW~RuWPw^C?SvHp`*=&QNJHd9(R&(1xw7hHAX)xE zhfgox>HYn?dRud|PGx($eqk3oQThA(GPY6;S?Qb)|Tng*x095Bdp!a z%bKzAXC{ReBBCq$EwVb@l^1tMlzEasQI5)|w6VesFnQ&bu@XT;yuAKFK|zz2G(+Ik zyRS{YsO`;SK@PnrUW;J?8&(iyRR)Vbmz9znoy*0dop-H?-;WL;p{`~h9-4fzva-ha zwR9hgUHE#wTj^ugqMg3#PT|H%K?pS9$q1Q86~P;|w`_TaLvOt%!Fs%)7O;=2w@_Qh z&Z;~DmH;&jQ8?tn$a$soL9hGaa~I9F1fWmYEMsD&ujEI1Kt11Rp>hw1Sl-A1J$wm17YHn0UKBQW@H z#88C%65xD)hPM>Owsy9$v4JM4{Nc0Q!Gw0o^!%XRCpAL8kG9M%V)9F2NeQW>~{$&)TF+n^A@9Xd$N7A$^W=XqLYW!!yrgch=+m2tP4DLJPhgm{i_ZG%Te^O1` z$p5ceN=5T16YnwT7j)fAQ+)N5W0hi`bKsUHK%DAPAop=>(9IYN+CQCm|?-({%xrXjBFSHIhzQ5Rs zZlJz;z|+-N^E~W^;7e^Rg8zPo*nc)pE}2%gQHA3D{e;=wH+(>O2sYF;)JL=vheV-{ zoEifBx=&HcnP1U1cHQU1@SDkdLKeS$^|3X}?M^#s2!_&%+zPU*@Z_3BI(%zg96rqiQT~Om5&KRcxtEPsA;}O{VrE z8m(t+hi>K?E)*3P*-|$!IV5wJM!LH1s1~+7Zwt!pCb`AoyXT870L6m8@q+!(QNtc3 zObOxHw=8`8*ErO_#eY;xQbK~|*p+xHSD1y96NiO^V{u92=98vMLvv0J12lKoxsA<~ zD4hjchS}^}HO3C-E#+&Gd0AWVCh?E{goO&`_xk)4kWeg-!Fq^Ct61 zLV*V#0-kSNEc2Q*Xd_W*eYt{ZzzdlI{DgZ{Ikpu>bLUll)V_3k3MBjcq8P$%Tmk#p z=+ot2^-J<|mqrtAGOqTZ+gl31bIw4{$iyk7rkoA{E>}&QKO38goHrfj-wCKI|GhnBQJ@MoE3mAl>0x zbDt*LKd`&j+&`+dZhGs}dum(i{EaZ5SSgfsM{aCTg^qE#_4=6GTcj|Ly$j zPC4B4J35Bw3M5OU)wuAix{J)% zy;srt6eH-lJ*YpG`Rv9czxZ{j?UlBxe@gKJe^H#Ni0S^(MsL(kWt{XZ0hzW{M)Ay! zi98qg+j|1e_VCF4(C1{l)g51-*;U$+`=+vJ)NcoSyA7w`p$U?J$s_#?qBP?f-UlJM zpkIFeu2(|^FAo0{`p3BIS)xgtdUP6om9f04|MqwD_FAGi=X6j2Zkq-JG}VQ?$Uy|9 z*U?cB1AEAzNR;Vtc|JrjNs;g08R-tf?^DiTchW+O6w1V zO`YryT6r=uH%b)ZKB2iBf7@CgiZT#*izGcIbQ6xTuu6{$%9z7Kl_RLc{HY(U{7~ov zvH1+>fJPJ=McMXR^wZ;@LOwpe^1t?1b-IuFa*R2fC)6VkcRPUk<~p$GUg^s&etQGp z%~BJKyGoSOfhf+FZ?Pi(z$#~iGYAsEG`!$!z*0}v?(oVr)_p#|}Tg#Z5GVh1b@}mH#@|v$8NaO!2U(1Wtzx zM4y;kEO}xJWul7M*cZH~2NK>qNc*JSi)<)65)k0aq2qFZa6u4+QCmqM2QJy`9^@6h zc_VAp*UP7zS0LtiLtDG^lvXUigW~0{ym-v!pQZ`jdE+*+$oXZrs1=t8~i zL%{wt-f)_rlj-#(sf*Rs_giNup@~PzJ~QEgecL)8=GA3m&7|7gY684$dfE}Yt}Z92 zE|uSD{?h#P(@aI2v;ZO1*#@pRVlC~*#n{Q=`Rz|yT609*9#sGSHOll(;YMNAy6kY{ z0i$m~(GSXHaQdlSc?`$1bexmgMzt@s9Cj1G13rCL9PT91B;q$7CPr%W>2=^py7XFg z%WB->44sLj5Wlnh6@S!fV%?1L@7*=d=R*prZQCWE=Bk!QBLholtR*;zZ=G!K)|{Y?Vo4TT6*#70ke zqqtdDL9@lm&g{yP*oC5o!oW|xeSb8lSCB30YHQSZAJ8XD?cFc?904#POjN+_)_qNL zb=DuHe}?J-nERY#a!Dq$osIdi93{T_#$Rb|`ug&!|FPD`By>%_vN@mY+cZitG@I7c zReKc1sCvs|HctPH;n5?)SZ^)oiQZJp5Q1`}xkJK*x2LY#&T*M;$*& zf(zN$`<#DL^_!T8^v=E@Gjr4&kT;eg=yKY2+-*!EBjv|n+NG6bltv#BdiU-c?_j|v zaIR)2nDq8;{U=6(L&OEla`_@2)U^heput_x!Tq?4wa0F=m{_S#*Pi$jxJ0((G-HAW z2;F1%;{$j=sd-Piwh<7HoCokYDCWTf0CmPU6#~nlz#(P(){;lQ!!lCvgX<4`NT2YD zg2LPCG@27h!8)6H-I4Lxi=9EsJg*{y%GhcH;<&Qkb(14AH-gCC3eT4{m-+NTSj3Bd z?ycHn!4R zGjt|UBZDdsmJj**JZ(FIO@fKX{tF^+HSe_P&DJ|Tq(e4~YETw@Ug&?@(BN?dLPSca zwOIBV+ndNgVBtKzD-24}A$aUn4Co%5l?rcBYnun{Cw!0)bTS|MW=Z_2OGcXg74l|e^IUcwt9Cd z1AvlGZC`@A6GuO?WkTNk5UL0D|JtT(pBnxtDq1Wg*^16e8nE2Mn$_p*t$VGL^!OKJ zdzj#}*cV{s3Pb8R1W5rDP}8uxyDQU=5p&b+-{_QtrHOJ)9ldpe1>>9Jb^pr{qEdk= zW4dRtZoYw(z~g#NF~<`Y5@~6|O7JT%r={fZXVFn5uJ_NfP6G_wOy!y0-d=um+Hh%% zSHBpXsZF2!o}bx!hCk-KWd$sesNt%pDkg@Q+r1bOXsjpowBJN zE{v@bKn3U&3=0A#CiQ)e#Ef5t7M>u1pjoa66h;nf*e|n=J4K%;R9&*;K`60&R#{Iu z=KE^m!S?U?O3@z3w*XKu2^!@6of_!4e#~>p1CL|df56GX@zrO&7Ruy|*ta2Y0&#L? z=@O*)CHC3fqtbV8-Z4_@e7Fj5X-$d%%s$Y73KpqY|NR@{z+Z?9A;@KpOte+k~zGnsCNw zO{yz=kXLG%A|NMDPfTNj;B>a4Ubib@x)o@GaS;0ftRjhwy-IiPRjLU5Yn1wIu&gK>36jI5*o9%MOsQY`vH3q_fr72Ks7V6+Scy4u=0 zyiD;JkN2F-p8C3NkIX%toSQOTZ6|&T5K?=Jhw4i4G)zCar!!xIo7uX@dvi$Z3@am@OK+u)?YKz@3TS_Wcd|(|c_gK`) z{ee6yVjdT0?dW#2S!nw5mjN`G!kp_>0`czhl)RCXD>45B!fj(Kep&x=vxB_ed0_Gz zF5Xgs%r+e){QOGi9kZj9gKR?pifEy%-T!)cN#Br}VPgQ7lW~rpC9*H|@^Mu0V>bZ|EEqw8f4c*_8@?~t~Q+eKAp_CwLr)xoFjrD#Ar9NPgD zrBev_JagKY?m!SnC$T{)`L-Z-5CmzJC0%88IfwVQkf18A+U6fV_VfJj11OMF@xjST z4tQ;$0C>6C^{@Q(R3=J4j0Q>uhZ+~^?VOIDr0DQIV(-TR$;rtAC2zau7ETR2ZD0IH z_hMce|0}i_QRY{~8C!Ht*m@Dat(~O-du7#jt4)1J7p}X3NdW4f{f)L%;yyPO07yXw zga1&04Ks|MSvKf2K@G!9b1KjiMU^tU1BSxiA~fiJ*_x0+u-Eg7zj07)f!{;}RP1wr ziMo#!P0mn1SS2~F!_XJwyUoJz?*QSm(;PG zBtD*Zt0y;LgGzh6n~f-O$6FV90Ok5C7#-6_ZP5xv)+v{?3-X0g%4q4AVZmD zBTT<~e~IyG&mWi}51IuxF%f`hCN*UM5*~jvpfDVODxtwlLSs1TK^CY`p^!bb>~G4; ziRkjQl=z5cb8s|B2gLPad*}@s@HwQhsi_GYjfPpcSplJ-ynKi5@85shquF3+)A!~l zu%w|b>K0_uzhyCCc0-%itjK2dzH~i5I`n%*GzR&;>U?>{N(mpQgr!6`h%I%Ew zr(9G>*~awrBP63K` zHb%k^jtr+m*ex-3;?uAa=%$uO(vc|B{sUI_ZZk#+Vv%w`ghUYlOzIMt`bsyss$Pix z9RdvxyV`7c0O%=GO9|LLU?YI!;)8avDK47>0X1F(BW?`st)Ex6P`~O=ciKx#Ktm@N z*LO!vhdfz(z)r_&0)h+bdQk=172J_x;T7E0B#OKz{EQh7LIEAi+vL&3pqRSfWC608UpNcV^-rWArli zn#=pS1OQg>MsMV&Wj#b?zzUki3|wkxH->z%La02-dwfx-B#cG6-BN7|S>@}OTC)9Z zd_j-_Wx$@~kEZs z>=If)>NG|?eXl9<`r1Tye+CRgm%=ZS0yJYK%4B&m%u%6}VjOl#M?;fA72C!y>M={o zTUoIUd0T2Az}nggV7Usmr~pWFnLZFRH{LJj5S&2^RidVI7R%%BTAV5l_RVLT^xNxU63OEmb z*^nU^Bw^TBH6)roDH^4i~hJkK6Z^K((kLV_`&e-Fni_n z3Z{D9((-* zi~V^&o&Cp65jP0;SM0CEb;xcSJ^}uME{cM4;ek^b&**gJ3@#v<-^r_-OxQ&T3y}Ws ze1;0)wrQxV+sdXfj1In)D82?m#lqbYij&g#a%$ccLC_{4V*M&hr}n$69iREYu-UVU zSCp~wiMqkJ!M)-PAeG+yT9D8E9<$>bYfZ{M0pRD~6i$Nak1XV&YolmGPM2h6QxZ9H zXYMK7tkEP5h)^LvogA#7w4V3ibd4&`jQe=MpgWv=MUf|djRwLs2+8%Mn+>&#Dqmm!y!Jmq36=eU%o!_zt1qJ7f?ViF zD5O~D-$wa}i2O>KhnuIt=jmR&SPC$lzpo?tfh9r~@5k>OOCeF-vW&~`>f@gZL?5$Z zN6BG5S-=PiFtT_P7;9fxo%P*Emfb1-Tt{&GJySlWDx%}Gr$VM`jk}6){YeJ$E6gs% zVnyKeEVf+VUW?F(jF^CaAPh%YH7MuF3Qg?YpF&Xx30pi;j20DV2iPkqWEzD~7h4Q5 ze>cXkC)9Kx>3G2Qr!_gTy{RdK165VzAKvn$ z^jB3&ZwbmWe9_b*HldYH`<~$X*Yya=7>Bc{5WVZ$1R{|xRU>S(82cwUXjH8B$IU&K z?gvNo&pNAK@%?lTq|_pY-#o!GF_W|nsY@1iKFe(%XwGLVly1H6j?a%&)?pgCIk&g@ zgoeH2TM7vuDl9l8!gIS){Fpczmn&xH8mSJDrUiwC#AGb)yyc?nX(b-bObySnyR)Qc zGJfkO@tY6N+zL(Q?wOepS5g@!jcxwRXa1OIZ}d1@`O{c7qMuUGOX}4&NtY6d@O1B7 zhau*nN;!=EB7KPtO6_yImToZhL~H{`nf8KH>u&PV>H*RS5V8PX#69kpHDe=x#S^|+QrS}t2#KM(7DU4rx4*?Hh1i&@?3Jq9rg$CV@$N@*PYiB&?_gE~GBnraZ zE5(5?Wo-~31(d8HtNFeJfjSuKe)V7H#;PVJC6Nv+1_~<74UIpwt-vE& zHfpyNqRxd{dEk}h-V6#=F3xhQ!7az0r&;f7ttU}2$Gms_7^IVaW|IuWvmGtPaz2|c z%U%VJ1*vSj|DhG68U97b=3qGp&mFn3%GP0Ug_4ADmXc)i1Y41Me%%x>b6tIi1*r(g z10Ne#V&G3OmE(`=0w^5YUag%#hA>JACKRW$ANc*|E17n)*~j2`RTBc#tHWtg`)zfX zJdN?vZ{`t5dxaPi8g+S+uk?pa8dP*HE{_SlK)?j<=TbQl z4sP2CC<2c;dih}wy6rYy873jphxzH22a^vsM+C&L0kLF}lfYkpPlD}&XhXK!WM+o{ zcw+*_`f@j7g9guj5g;#?gQ7^g=uTgA>(gd4STjTz z-!`C@g^)Wc&eb?%yL3plAPO$t7!K}^xxc#~N_n^EqdTfFUuUZ`e7-f{G<1>W@3_Mi z!E1h1^rf*zL3gD1OKVdU2=^i_-}6S81nd|+?Wi%TKIo+;+?6`r16bVr7^pHINt2ov z-@X~RNzeB9n{ycxo)-Etz4Bzw!F*s2wMq-@#k( zX;1K3yc7QE(Kr0D*{L)>1q97nRUUmjUl|5qH?5cew%X6XA}}};h45)RPH)Fz3h<;j zOl+@`&Mv#i4&0!MHhKU8?lRoRe>5~x=#Ion|U(WzD4 ziY6x+|ErsX!{U|B*tR6}{WcAYoZn_}x+^kyswn9BJV^W%*ZbdWdURdS_wMvTI5WL8 z_YjK|7Q4oQ1lEm18gd?~5$7EdyQjph1abwBYXRr_h{+Ng@};I)>&`1P=9OsMSi(cX z%pfW^8s$6piTx+RJCu@G{ft8{ur(>TmfiZ@wn*xwf!XIENg*)pUbA6=p{c zPEQ{my<>~*JJ0D7iluBsQ|=AkdbP@Rr2dEe75Ezb{`&aO!4s7s_MDAK@`1YDrKno5 zE+fv>7hdhN{=G-mb1y&ZRs53^Z~3mO2)}Ft{+W0Frm5l>d5*pea1@8bo==#opYF9j zCQ1nMBSN|1yMswQD|ZPzP}gPmonn#8lyAte&gcHxU#S|IG*<$4gHPX6Z?@+r2)|Rn z&7s6w6LE$C8)Cjx>047?1o@7Kr3@zd z?^fG`X+^IiF;)cR=q>M8R|6!Elb`1*%WnTN(cj~K6nr#5PApfH!RlK>3S}}|MIJia z{#>I%%N-lMr&9WM5yR+rF2Tb11$op!`D0CdXemtuMTsTsMaY5i?hCspgilFQ9 z;tbpr6No&Qe%ogbh@9r9!eua_a7Kah!P=%ryDoTL2D96E!<#%(Q8T38j}$ldPb>n~ zi&Q5huB${2QVh@q5I&@-A)lB7bKMvqBWiJ0EWd0$qzyn>Db}w1RZF0h#49-QCr##K zw&aeCy##U@S*IC(Qe73jpNWj7HhUDYnN0YuCU)uTba$s6GXdVp8fx+{i`8RYpY3GE zb?vCQHZR>sfTD~O@Alij7^K>=;%V2^{AS^`M@Tth=>u9P=>Oj2ZPf?iP=1e!5vOFO zWs_7*+rRDE^6qSC=S&81aXgbFW-x3XQOrQ<<=A)=yY9Fmp9H_J&8BX2j;f)U?s@*) z3-HaEfd4fFFcbCj;XZrmW~#*96z-M&=-r?jgwk|UNdk8ejb zyu&E2iQT*6cf913kCUdc}Jh z1GX$26RNMmE^WQRCi_&RL<7Gsrh*!`v?MND>V?B~?~Y`30yQ_v0IXkqlnXu~a^U$v zx-U0h_!G67%D9DgxP(50UvCApZkHM8I0Q+{ymcP&TdwC972dnu@3m@6@=9O3W_ILV z8laJjH5W?5`EZ~i-QS#^`BM%-*mdii$Z^SZTn^4Z&lQ|D6-|-7LX>|oa4UPKH?eDH zWOD8QI<;!J%Sm>vs!@lo#;C#K}14tk?sP>~TVNFiOS3&F?NT*=>=38*tyXPTqNYk*T@txNM z_`Mfw9Y+I!1lq9eg2J)nj54+~?`}4n7;~6Mgj$Y8(fxstHF*H}m20~1@tyGGLtrHN zbb0$$&eukx?$=`43%kGey|J^7BZ}hL+>h|lcUpIHiKZ`7*G2SsC1AtR{NFDV)v}q8 z5D<-$yXo;&lWZ=X=Lz|bpgvFIt-Ll$Yq5Q7aj)4QWDEqe;y;_7Z=>BjC2tYMQ@5q4>8Q~3wF~V$e2pZWdKEbufe~#s|5WEnc6_vWB?>f}BVBVw@yB06FL+6vRc6Na((6$}Ez-is zWJ$aBgw|FU%j3;*@Ial*F7AQd!mhP?@%ND-mhVUNTh>8m#=Xne3$M_3&Ov(PPg1xr zpbR{A@mjAg*Kcg)a-6P1_b(0Uc?n9A1vspT!#jwdg~DkJU|=kI+xCNvTwdU)mcg-q&9K;2swR^yh% z^YCZ!*aRg^h>RycVurFW+Z$v+Y>x}r!)rM$@2j98Mx^hLGPRFhrhSt12qwVnszjTa z57QwPhFN4I7rYHq6Fj!vzJHT_Lh<=Fd(FSx0PH0^#sazi*c^15jqEcM78o^sHUM=OtjDJ!Pc?#@l?hx#LFH8~4^p3nPBr#**x2creJ3Ms`c3NqAW$ zUE05~QP+0xmQjI-geH`TE!8+%a=&>a;ak7y(L(c$H|J}DiZ{D$-DdVfzxl~?Zy8?Y z#11S@1amITDD>G9qQm9;+)M)SWFIOLBy;&ZBI$U1ts#@mg5jcXdDy*>v1t4icC z@L;@$8xrC>WNA2Q{T+n`pMB)-Tbxbp6S)-(azNsv%5n%o+$X)FMm1;S5_nPg&D(YO z=a-f9NQqAUF-EwG{%=jc%C!Z29)@qh2dCnPXk3d0b&CY4^}rvKnrK2m#*Z~8#q>RS zf}^qni2P%>>pxLc?DNdD@Z~jlas*j)kF3{4rC-2sXQEP=yA(p(-NNsTl#8)(?s;ue zz^XGzZcnJr+IMPagY)Zl*|9Vt=PBW#nejdDp+E9o4)SMj7@$1mYAfUO>uKFj1a;;{+vNT61CZC_~Ql-s3UGL>(@u+t;@=PnH>KFN>+7DZRJVRbS<%q6Z(VBQ$^4cc zbH9~lT5H1BNtKe44Afx5{t{QtHB|rTB=?~*#Q&@?FQ?Dm^Vl5p*b{c^9aOt_nL5$9~7pK?(}Wk~7o zSv#3$_a3u%esFpi0ojuT^SuT}!mYmieLI7_{CXsY3VzcI#6#l&?l1B;i~$HHBoL7s z7#1%#pdm$I)RP;;t#>#CdayoyI0GC}$m}2(AsiXRI{`42PHUKc4K_i(& z5eU;Dh_Ax@Chf>EiUD1WPCuVywjItsHeQ)}`%kK0vTJeSRSY-Zvg1Ua&P%1rDE__u zr={B5j#;-^nJ70eFyh#El9+6I>$RALvp5t7xZ9^R@{iJn`rVxp?liw17zkV5GzaWz zv3K+;Vmw`ylSb^%wBxeh2Hv<|9L=`1o=|3fNUM$aQ>vX(2)khtEg0l@dW!tn*f-6{ z?fa+lXDRRf^ZRV75pD+_0>#aADY(yhk$ma&uHB){6y;gEr*90yF$ut!sqiJC%jfy= z&WkvM1UhkNYz72cvp5Kl$mvQiO6$rz%b2T1e?kl`R$-%%d;7b8I-?rXKh*f0p5GX{ z4KM=L3fvGK@pfpJF4id3Gk+*x>D1=a%z{;UE*r%OQ!-Lg!H9HhwmN{i*Q!F(PQD{4 z$aa5!O7#yb=wH=~KM8i?2W4ml6t`a1Y<&>2EHXt|adpTiGO%bM#_7?a5_Ou5&Xl!B zL5>`hqhO8eOkVB*RCxH2MR9OO9XMO}&5xNer?-5kQaEGMdTnaFZi-*Ky!`r_qF$y>=wwEy61o`aU;iQ{locE}-buSR{sQQQK*4W1KNP?$u<|Rl>=awkWRvI>a zvq&g=0o1=#fDOALGEKJ6ASUbpoe*&~(GDd#rK1$NkFwzJDQq17V}rUvAo?BqM*|h} z$W}!g*hEoA3^Eg0twC}Oy4l3WE1dwA@5j0Z>*?gVPLG^<}DAM?G;yuH^BK<5=Bf!mY zb~LluKxvvq{I`J4Hw9VGdmD18w`G_f!zD>P{9uxI3Hv@-7E4uAT6Ss5BGm!L1sbCxgqBwu!Q|F%kD z=y%hQC;K<#<&}RlzSoCy)QwlZy#UE70;U=8P9visxC`wid(f$z0UiY#SSp6$(p4o% zl$yOH;1oIhXlz7}0;8V%yP7lHaC{yvf*_{11a^8*Fjv@)NGwb6(RIjJ32O!O@0#4O zgj2Fh2BCIpsdlrg{hf@n#wJwUxj2ivu~Pl(?Or9WnSkA-fYX+QWqj#+g&H^qQ#g5h z&u{S<_W|3>hSRsNuM8n1*0R7yGP$B{Uo6rEK9I6iJ+dAa z%n|P1?Yp~(4C^9=HUt@?Iv0Z?`5`zOv?dtQ67Q)%+30E;nloS&KGh!?c&1Ij5YcXN zf9S%$AqfvY*eIjIZ^c?~MR22{nxaG&&E_<}zY%zzOhLPA^Jm@L%H$z0a#8kMidvHJ zH)G$77oDA9SDM4-vYyYsx)t&W7JnIjVpWp;(Az~-PPTrXoj+08BkTE2C+nm6Toqc3 zg&InV2ma-7#ZTSK@y?79X%FOgM8&qKeBUTnOuB>$((0;czEDh-rcdA@GL4MWHF4V@I>KeC+nQXPm|M|$8ZIenyO{T+j{OQk_ z{B zZ>QiUHp|d#O+^S!wLo=LVswdPmljTw;L^?|Yjj;-DgH!}7a5s}l|Je{)#w_PHeipk zT;*ILa-Et&SvYtoTQ3p}Iz6+C%3aRtEJ*mcxuM`3DP62yMv;>sR@IMz?+w$}o>0><-~g>LMD8&ipW z&WDXJyq=D1!|eeZe6%AV{o;2(>&sf%_%Fse%K;sX32uVNOz!rq<$s+9qvv+$VxY-f*kY zX({aQaiy!>#QI);uYV;`FDqlkv)sygQ&XnAeG|qfYV<_&0{+DxNF@W|8XJ$a^OCQ5JCSrhUU{r_KJs_rEz55Yg3+3eX|20k&M_)=;@orD z*Cf<&`N!@H{TM~o+|&~-ccSN`k}0juOHI!19qMKz5Khk5Cy!Pt zlvl&AD@o41rxcHTU;O&R6>5;VuS)VnGDNLM^lmq zi1+6;ro8i^wdK4_?--*$U0nD`7`7CZw_`gmz$#n6{=?P)b@)~G!;_s)N;rlc8E0X{ zYjL456tohr)D*S)<$28k%#DSUp=_C{B4Oi?%F&}mZvFYgE5))=tYhb;pR4oAFWlE} zH0H;L(#6~PV|;oyZ21{}oGrIZM(PQL=U3Z|bkEKYzm-35RC!VvnbuKLy1UUy>?lua z^BmP2tKPpPqp7Wk+`F9`;`PMJVwAv}n@b($#7UL%)p|CKyNP_hB(A@u7Dwdj(?Fy! z&Y(wP;hoiDl%oi~;3BYR0FknEm8#j#AyLTCgg|~PawGEmY@j0goJ*~SIGD!Q93bY% ziArdhC-Z^Oi}#_$Ju81miGee(HPU2$C&lZqjxM0 zeC8Hj9JHsS+F;`td3-|kWZLc|cDnl`H`(J*8!lIl|<# z)InI-^`|L3?z_rYA5$XC0=K{-tcX*{&e2UFn<2bf=JPxg+6Lt$uw<+a?(pjgC!>$$ zr%njz$wd?&R_a~TeK`1+3owG(H9K2N(-t#i^$Mi7fxLQgVGE zy!VLz@MoB_Rsa7xVV848e{}c66hM;)eft7K!}*>Fodl6Gs6mL-#)0v_c7r*Y;-u}S zwCG>E)GB^-k#i$y@!xUA@zn)zb+O=g%EDN#ap|2v)S{magr9c?&hZorT7=xR5fKkb`n%S2N%Vvsf~T*= zDV+11HgGIf0}*ulyq!LjPAe2t{4beFrLNw+1c59HUs2e9gj*KI%s~}gRZ#8 zq(8cFy|{-wj>Q*!sJnf=-LDQztu1;4({}Euw!QI>{>;^rNI&kuPw4O69onvb;mBIM zF66!THTtJrXVgBV-)$#hxBR?SUCc@|f@Gg)g%F9jC})U%*-(+Vu1SMuVIQs9UGw^7ZnYXZB|ic}yDC3@W4|G!_O|6cRtTzo-ld-MyH+UAVlx zgbY7OBu^P_^4)Lm?Y(_GuJae09z_34jNs_&#|kn#S1TXZKnQBM^;N|2u3nF$u6%lV zg^g2(o#BOXeK+t|k02WCNUvz(_tEG8Sr%Og4rqIZE+)lqE5shg1sJb9!QN zY0=Al-7sk+sJQT-B1-kQ?V&a-Wei^0b)DpIM`ICEemjv z&9~A1AdB=7bm$~s;*aSRWSY{`Voh+=^}FfNPHho z%Y}N2ki5@9{)yUp_v8qFaeC@0zqc?vfplUKu#@Z&}D9REYG zOwp?BnCvZQ1uOURp`2?7e~i{{v%0oh<+W diff --git a/pkg/ctr/assets/quicknes.png b/pkg/ctr/assets/quicknes.png index 151a8a8ec6531843abfb2a236e9c1638e61ed886..b17568a94ebd4ad09b091f6942f1d8573b67fc9e 100644 GIT binary patch delta 481 zcmV<70UrM43-AMw8Gi!+003az3AF$K0ANr|R7K|I=H=z(&(F`Yva+kItHQ#<-QC^a z-`~l}$*rxe#>U3u1e;&K6M7Qg&u~{%~!jJmvxfD!Tl9x_l$7LzFU_o8xKt%9Z#rKD< zPl1%I?2la!6fJlca6MpaS7f2jx5I}kx^M!w$NU2y-$T+jt`Fnua(VgYvqqwby1r?9 z9dmu#J38XJAMm;Db+pws1MZ^DyqOXyfUxTa1I}|G=vum9d>(L5Wq>2seWe>{X-}>) z#wqr^S!J1a;@rIvu4v)$l?D-2S&L1j5o}TFyeC9;&flAeq$;G8rDFu?2!YQH6)*|_ XjtL<%Gc$A;00000NkvXXu0mjf*dp{S delta 1502 zcmV<41tI$I1LX^l8Gi-<00374`G)`i00eVFNmK|32nc)#WQYI&010qNS#tmY4c7nw z4c7reD4Tcy000?uMObuGZ)S9NVRB^vcXxL#X>MzCV_|S*E^l&Yo9;Xs000GINkl zBuPTobyBGmq9_sw1P-+mPHBB(`Sj@%wOS2XmI26QGGwz^1Yz`2zZ(I%t|QAbilPt- zg;-u*CLWJ7Gk-H<+3&;zy!!3;Lw{fR`>&DhG)<#Ysqp^&d!o@Og+hUag$2j+oXQ(8 zO%qjBDVNK%+ienw1gop7ghCv zRI61KMZxFuk-9(`ljQUHiT1Y>#tLw1gS}o4MNz0!DtNtKGMNnN zbb4&!0qAQQ>ed$R7yr;x6af60H1n&g#MajlqS46(*wN{9s8lM{>vh86Fe@u7BoYZc z9*<=|?SI)bs`no-=ynhNUw^xc{&ttsP)6Kd;|S{pL)Gcxm*V94^~#FTYp=Q7Y|^3{D}JGF^L;DEZdD-Nx$Fc z-Me>Gt5sJAD=XUDFOJb?+r5B#z0R9AZwLeeT)cRZGiT1Yo_G|!TFreQ#lFuW0i{w2 zS(Z6__AIGX%Jqbn*tpZ^F7|y61nlnaq9_WNE?pYyDwFuPKb1n?-E}3vmr6Pl&}=r5 zB!9^=({KTtXM8y-n?h&B18XfJ%2v(_t^S6GcIh` z8x9j&zi!)ZMp2Z5Bfu#7nnv~h1Iy%dfN4^_|A4;MI`Wecjgq~&;Vc$B9x@vncpW6j znM$QXE|(h{Fvs=pQAF0($lbn$*W%{o4S$Ep-MK^L*I#VsYPZ`6-EPHcz zB=sexX;QD(ktB&;uSYtaCZEqEd|fCy!U_4eQ|yj2g3sr(9N?t<+m2zKs8*{H2!8}v zUS1w~To(+(upH>5{CgZ<-=oi;KW`bMg0Ab91IE_ZX+Co9L{9ztYrei`IK4*;olXbC zFz|Z4qZ@qoZ|Ux)eJ*g}-*B{mn@*>>cI}$0d$b@30;;MKkH^P08v1wj{sWAUA4j(F zhQnkwHXQkPzu!N&N1LXJD2lfC=zno|o<4nQ?(gqYEEer2)>;$!x323{Dist(ArJ^4 ziXzEmaw6CAlu9LYdwZL6=gzUXxOnV=tQZF7-rgQ_b8{>$ElsV!CHVb*mX?;-+1Viy ziC88zx2UR$BuQwRMm!$p%9Sh3&CN}1eE58+H%*gQuU^sX^(YpLV*}a=!+(q^%kn{B zX0zENUsk5YK~=&q3|_u`iD4KN3I*4t?qOicvW&;$IVj*BCotV`sH`#=4A|M(q0wj{ ziXx&Y+AiSBz?_|(C6~)h9GGqxSr=+Ho0Li=bX_MFixG>(2nK`rd_FuL54x_?>2%oJ z+oRQL5sgL>MRDqt=VJ>PqF1Uas;ctg!v}O-XD}E5;Pd$i27@dtED(>!T@|>djjInF z!*kOlK|XC8Pl!2tail*@+Di!G?M1t1E$uL6%F)0S^y$+-4n{&(HtS|GP}z|6iJr`7cBOevcmgBmUEbssD|0O-!8N;o;;W z==!?4e{_N-Lz(y|}1|pPMHyH`m6>Dm5h~ zQ$^j_&?wKuOkG7KFd!f@JmRLh`aL&yQ)3ewORGe_tG+%y;=&^7*JMJ1pJpnmR=jy* zXL~nCS6@|0sUSc9o~zsadme&({0}@mwQgt-lH+6J9PZk^&VSv}(cx%sudl0n$H*u! z;7NT#qMWo$#Y0~!bBoZBP(mlEqLS^ct*%bab#--~_wM=oK2}pwmJ*i;@biD2pWoQn z*ihdFGACymv&SK-xBFt_2bDi zm~eBd$`jL?>b)!EG6K=nQ$K2=9DfyK(8;A7c)xuXo*v(I^0K1K;WX z)F)a=Q*@trY9Mk{5uDPBvm-o!HwFWT^I;URI0NzqJY=JOSqVdIz?Jj#b*Dt#nT?ry zox8zg#z5f!w|mXKF}I2J@v-seeLi0&Q^v(Bw9OOiW^z`2*7ko~U!HrkG)LXL+jo14Lx>33B(bW(-B!G6eO2)3V7!f;z2lc`J2mgfR) zk=)5}*f|l>7Bhfkgu&vGlOI4;RZvG@jBZ9<^-HeIqeb#7TJ4)6NLxL+HL74mhy!dO z@n~>lC2-OaNVlqaVE0wSW7Ai3KqxL;Sc)V>TNr&1o)cS&6M#pL!lb~npz33M+6q^l zY_(%eh&XC$fX9@+aws_2wn)~bLGadY=q3@G7rgj9rbA#tBB7m@>s#YW@55nUO$3B1qc>*6%J{rSxXL%37Z?<6MHs(lI$>csbm z#IsK>dLY!0SNwp=`Q397w{Jf#)+8Q58+6>Micf_nS*-ywQbaxs(nk&q&~t*fup8$F zr;)`0U1=Rx`*#-U=W0<@X0v>iDGgj6*hD@Jy@6Q9E3-$i!;Q+OQ7O^>kP&!$HX&@r{E{M z(OV4(E>-buVro=squfHc?kUG#yZ!rXB>*h??tN1>=STto}v z#FCiAkfM=*0y;JJULsF8g-f;)iSgV}%`p4~4{le*BqSsxcIcs3o?#(*C?BZisNwU7 z0S|Ux!7FB;?X06cfvmeZdf>BwT8`*WRLV7uTMgWJfaXPN;bT3BgV7NoI3jCrkQqLG zPTHy9hHli|?~w^8dLCO?Q1CJHj)~Q@)3nn?n;b5n2vZW~C7EnMe?gn*1h*$bPDIr*=VV)@-6ca@ODLG1UP`?|u8He3%mu=K0-&7y1rSYY`mG(-D=fPoM|d3H>CkB|hG^pe#r;|M^qPHru*N`qD$( zMI2ARBC{t_9S6^$H~Gkl|AB)g?Niak2TcdoWhY;L$w0-xK$XZpz+-7FGH!fDSnmQy zF-DCK@vCPRrLT&FyAS6Xfmpzk4UTMxR)Q8=U#wLI`}dpQxzlOtVHEk3&XAIUiUzre z3k66|`W!SbT=MaS-#~>d=KejO#Ra4atn>YRc766L1A{T?4{iPTONrIB+}yV`6mg^& zIQRTbA#~pmHFd-S`b!B=A?n0}6>$AjS>qQC3FoHn4ApUmon^!NuaY)}#3+V|&xx8C zJPwP67sFi;Dv2Cyf zIgFe?{1)-c;I}u#+j9d|@b@154G3nc>Lxwx>j-Hp@wpA|ElikA zo?HD`_OCsXaJLKmQT9f2W!1g1@bhiW(~r&%YvofVKND+ei|-*S>zSt~pC_NKj|p@! zF#DeLep|mvj!)4L#J~yS}a7-d%6Id_G?*YMy;oE`9qO zvF4!nh7WPi%)(lW+`CSG-CWcmxAlU*XBM(*s---yR^j@Sr!c#64IAL@zBs3uC`4hs z9!?QuY3X<3Y~LV%+8}+qV-ze^3`gmFX}>kPM~*T>QzG{O2M#IcR%gJTaK{nY}-tsm1trT<#US z57)4-0?+z*vPFWoEDdu(TI1BHXqA^We^M^7;4G2gcP1_%RSbWo2eaRLPJM^3Lsgp` z@6lRdFU_U7r|)=PIWkXX0(>Pw4vWnt ze*OLsvewAGKA+S<3N>= z*Um+~x(pDIUs5Cp=C`i+*i@phW@B1qZ(HPcdmit(^So6axp>O>qpi)#9}0$mOX`udJ3F|1_l4xbv(|hmEQh&P3A*=-JUpIL3xkKMFl@~V(2O_Fd>38 z?!hWv943?J6nYB4e1DDPtJfSJW#$M{Y!=Jz=XRFcWIL~AI5T1O#%-Tgm7`(8ii zyga$hE$*uD`0RPJ=L>wN^<{0&4&8o=GGkpv&!ymwhh1U!k3 zHT=V5{K}%dVmua#9*h3n{{o_P1xv|1j%5-+(X05E{}W~FMOOr3hFQ8P0{{1BX>e_~ zAe#{R{Ph=L7M}3klO!S@G?9aWIGEdT{mSH3i{(8>{}c{-*r6j*{Cpr z1vDF_{$6}HZAym^oBsF?Ku1L2iwk(V)KGk)>I%l5C;Xj*@xx1l7q(-P%la;SKT*ZD zP?&VGZ_Ykq1b*0h!d|j!Y=#}MbUT2tMjvM|Ezw+X#r7<9ZH*r~RcD}o_D&P*oJ*pD zwvK}2{ryzVw$E;noA7f?{rC4B zGvZ*ntkqvdu*K@|7VDH3pq`u>!x6&@rtPW&JMIj|rvsssShPc&sBb6T z<=$&A zCG<}kjav}jOm;sF#z+UwwDUDPAFHBp9u2lES9ND_CrnUuF7SiZ!o1;&L^h0vqey%i zImAeGFvJ7=`$|c0SXwSXi2!6#H5#n#LnaMeM4;l&LacHO{2f*9(fQ*}%{Y_eAJoxs zsQG*7-`cd#kw`ZhzQqv-fp{%>eB;x(t@G5#%-5~DSU<4_&T1xNW1ZE z4_#IG2&#AgDMP0T1|BSw4Z!yt&6+p>{ba#Az~@PgFrbz2QbSX~#!nW?3rH_$CIC`e za5ASqb*84NsTmnuA>gPC$LlbnD2KxDZw3aiK<`8WDdNFJ=&c1DkA+oT1hn^G2yhPM z8Nu|DOiTw?@|YgxdoO9dfa7M%C#}pBoe`qLurKuqG}gJ8=OK^2Ne~MMBTRfWPB>)(1>mE}&zBn*7Qwn@v;qlUlMl?JI~A1> zj8INyIyB&X3uw`C9CS?5S(bpbjF*gnciB{Hh>~^ee0X1E& z6iGVVT1OXGTmmv@(uzE$9Sz`lu?O;}yUhnd9wv;}h=mgY%MutD0dz}1^4Dm|iA-tf za~_@|in!onMxGFB!*`i>w?Ow^!s*yII4G!3@Zb83@Z?72>?;2hgJI#c=a-4C6WxOH z(K+?9B@EwAf?V+2Q&egh*(3mCz2%Vr{0d4AO_+g`lkQUU1Z51u^M^ouYhd0nS#)*hf<6b>+`|D?@2L?@?y& zN4cH7>Zb8RwD6{9N+`lM_a?4+%r!pR7|fD+w4ub6R?l_5GX%pe4tf37Bj3xsW-dFY)* zY=1oU=MiHBPei?~d&d(?Y(;lDz~uIlWEus%QP>+;8ik*nJR2N(F$|}P9UXnTth?zU zfj37_AWta6yUZ3SN24OrC@T0J`r3(IM^k%&gD1FHBF9KAj)Z(t7dP zI8+My{V?ny!2hf$#sNxm&j+S2Q z)Rd1Gfhf%R&Iyhl;O)2SKjRQlRLg5UJVG}l!97e2AUR|StVIk4N4CDHDSB|2B3QjE zdF|Q(_pP{}4!1C^CHf5apKLVL|J|}WVhx--pNHLmg;Kq?zdy@rQRm_BxBtgWjHl-q zPQ!zRcIo)8la10pMa@eca!V1O!TQ12Um%9ant1bqg`d9(T59~uk}g+63X51t+*UI+ zdx!Xoc_Lpoxl0tcSc7z8;&#>;l6by9dqFj$u!2IMs|@egara8984V7m@BBzGIB|jy z^Zp?;h5d3LYC}DqYY$rJSYon3DI|fhmBwOLMhyi#=&tvNfwV7OhwEwaHl`-aL`X=T z6HHTm38~n&`T0WLNq$N}40r|ycz9}Gdj8Zy5P%s8Am$Cy0X9pJWJDO~KyFY8-~s4} zVD8?*+7DJ&NMOQ*hAC{>kyz1AQZ$HD1D`%@la12IPWtHuJ&GrX1V956kirhjw;pWE zSSzWaceK|@@CgrsfP;O}ZLZn}m&g%i+v7Pjyfdnn5H$`N(hab}7wzP3SyB&TSdeHJN(SBXRMfet9SOsm zqUQHj4PJ1m5t_CprbMb>=i!$<^O?v+4MFZ|?2T{n6EmtZ$$J)-V!wX>`Qs{461UAl z!cLB}i}=qRXzX}F52kg4v8D76$DA;Jed(M$lTGDV9T6|8l18PKNn(TR>4|xBhnQPC z(JR!K*F-G5X@1c>3OS6@BeLdIfwSWb*d$Om$l?Ir{b*x)3C#nn{L9FDkv9!x!&bExiwy~I)rY&;u(Q5|fl6kzw&IL};&a!X-{B3Ra29|qO(9UCV-ke)`f zBfZ(gH;$_*Ve>CQ@%39yzN-{TQ6Go4V?>)=G!n4vtSog znra*?8~y@pe&S>02XkZ9QnptrfP=WoOq;B!+>x)&(qrv+s*WNXQSs5Zw37lO;K#e% zgDt`V&Sl@|Bo#24g+)tzGA zQ21UgGXn=OPzLk9M&{RY>~$?y!)=qkdOnHCRtI~p;d(gWJ9PgHqrpqEmuh^htVUw} zqvXhDGbrg&2OX3Uw#*t_GTg>QkJ3sjzI-$4zNe97CPfYnV7BIm)BqAkQrdn+;?B^B zD$hx-CD$dNV<@FRx+L+dERj?co&L?o#Iz((Guzl9$C(#WSYLPQm5&S@bG@5$In2C^ z{}scXz!R5Nk(sp9kJ2vkd*^@MWsH|M+Pv>Cz^(a_As{Zqq)rMZCH%-0HGtwZOe(`a z%d$qwltuzQwFtx?=z9Y=)@N+3PG2ye>pIFgTzM(%I;mX zjALW;wzgl4ABzH2`m87+)8r3-UkrWe+!C(4lu7lq^)W*XkTql!vmYVqe_69(U&jRH z&zs6wTvA~Edb3SflipH=zY3+mpaj<<5w^L+H#`9rgzLluua=VpmU^WdvgMPC!lZ0t zAJfKuN%w^dE_8UN0qe59q0f8Bc}yuzT`GVygJJ2K23ToV_TzZ)xZYRptVDXw&q=;E zh*&dui4>yWl%1kndYow_^a$Ye!JfQ{eW5+&7wkAP>@|V zic~OfFgH{M#ZLjA8s?4PN@{31p6sq1tY((3YTl?K#(>gOkB1XG7u4&*l=E&ejHJ#p zS``fxkF;GY%nwb`!YOlYN7$N1JRf~SP9olL(JWW{?(zc0$*&U8{9@_FMDqx~qamW7 znQ2zbL6XFdFU`$C!tR%q@UqPEmpA^6Pa1>yh6+pXeXaPgT;>&&G=9{wJfwF$q@4-8 zUaGM=IafY0^Vr}?m9;x?hQI85)LwrX+?%HnF(oQj%a|@4xN=A(hg>|^FSl#V{V1+$e7yUa~R7} zYPN{-9v5<*w)O!@tKa%m9CwIAnYDam%iZI_xsE44T2Xid-I^w{aJ>$M` zC;8&SrZ9h7*%R!Fs z2uBC^ivg%T!SeN-{250!&%K^p$(W2ARGPE8h3lMVPYZ!5@&;9oAzs@Oy>n${nZhzb z5vw4Hy>r`wk?8G6`ej9k9ZL@2Q@y=c$Pdn)#~B@h-%Q;Bn+ar$(48KMe46gOn{cjsc$AO80%7E{P43PhiV+Pw z$kEKreDj6a@3KcM4Y4BzVYI}RzbW`&rF-)3YE#MZ^Y>rrrfm(DcYKp(YQHA=ft*UkNGX5oL@GFA8!WPZW@Wp+~mYD z7%8S9t4T4WL;??u=1}bkvhuTi>4^-jOsp{Y4Hn&kS5DjI?RXTirLE)f>qFP3_Vnm2 zDbSs^Gn3)*fi5nejQ0Eeg9Dz+4QAi(nlI3~m;IffY`Li+^qo0yQ{n3}OWU=x_Zk{( zI{LdswzKCRs_IV2Mc_+2dI@63_p(Zc;G{;fx@0kaU*6A&sb*P=`wK~P_WU@Icp}aB zel{R~!g5md~0 z_Qa3#1LU0YrtWgIz-}BVibe+4V;gmA-RCh8)yJA4H*>M%q-(SwC;mpL zJsqBfY559eviR+Y7!9;F3m3BD^9?jSlB9#}J{v8Oi~mc3rX#MsmYhWxuPQKh>PsK@ zudP9JIgzzmwP+X2(Z5E?e#=#6B3dezG92;qTSz4Lf#Htcc^Gv53bHqMNcU}y{Uj=L8tQrdRgK@JOLv#F_cJjF!k=KVPvK$i9u~Om_(>tQjCeOC|@&U~f zEe(fYe58flCBE&}tjO26;&4?+v*4$v^L=!!XeJh8Em_@%rACIm676{|e0Pe^=WwQH zuG`AxQtmA8PLBoc)pvVKgX+!gMUN8iNU%y?57*sf3B%=O{#sd-? zK8&0@ZUnNO3Q!2%)mL<&wNq+)WiG}I#V7Q<+w(VfIj<$5dru{cxj7cN*&~vBsSD>d zD9jH>#_4UB%}>46w|J@=_lveDNq0+gD;otod-#&4C+PEE|3a9MGU^y&tEiNw_RJ}h z@?b?YZ|v!b%8KKoTCC5*Q5(LXK9T{g&-$dkp#A6c+Z8&%;Yrov0=J#s_-UXSmRUIOYc2( zZakcN!gS8`tc6-q!^Y^6g2xW4%LEnHcOofi>!L|V3MF(2P2()M6jD>J#Vue`Tk zP_U`jm7)xxRT@nhp8_^vCf;eu$tFG@La0H#`0eP8dVw0>opj+s21lR!!mgFUHkQuG z=~i<{aNAX^v57~>^Q@`r&%q<_ss^a5g3@pA)JHGnnEa2TM0`J4Thnk~h#4ylj=32n z(AcD0VX0hMnFY{`&Rph)&)5O#OI-a30PxrU`4@oQ*ku0Je7TwfmJ5zhE)xpjyUvgJf zIfVn)@|Pj)`sd;f9fj4^=rXMUp1&Mw19zo-mu@~U?nMi@Ewb0N54Bp_|9r_QW+x59 zK)q-KBU1(r1R_3-AZW!ak8oE#5+C4oeZHaUu_OFStBzYKoELS&^nd4hB${ z6B%XNZm2r)ja;D5x~%EiXPog?34cRdLdlhX_JU5;cuJADgQnkeqQT$DuI^(=qv;Fp z-+hv9z7E4vz=&aVnXVy=v`I@DgHe&AOs~gf-$>A$&%$E*M}Kt1&b|_?A7A;k7M3g7 zZ5#5c*IHgE&j%(Z-p5Pj^QhL{TT9tf&+e1KgKEqB)oPw2==GBC9O4JXJh*!oToN%E ztLTPz)swd2E;@BMA2Ii150a}_<&8i;@4UuU`Avyyzs4Xs^^S$m?t7)$#Dgbwd(j(u ztj!NTGO#|;`_X#y-gw#IhrAl+7ZzcQZj%g;C@P#~I!a~A-qxi2E~f#}vFn=*Evu#Z zjbvjs>K;u3m1b?qo)%<$_ z-%(yg2L|3eVWuJAV`4ky*N))(C;$CG5KeF>-hhxVX67xBO`3 zXkujd{=Th)`L{hOW&q#-R21ZNT$6X2ToT^>{QA(d@Z-i_;%2YNsB#uc_!bRxLYIT8 z-PAh9KU9oce0kDpxwhK3N_KYYbJH2K30bwAJW5eiOTibQ=0d`_e4%&fxYtuR_ik@B z`Z%R*)z^5K)hluCUy_eeBP)YDFAwabt z`^4l#YxvI}4ggTd()x`dg(I$HZ+%{BZTE(g~Cm75fpf&pL zGrON*^mb>{=0%t990wj4jy_4a7?QzS>sEbuG5C3Js?2{NS%Ow|Os9Wwdb+nOlASWw zQn&*_cRYqO749YLFs!!WUlRkbFh=F2zCJ~-X>?+4@93tectlo0N&w0-so$O@w8E&f8xS(G78x!jpI7L0^Y0JH?qLlV%|CU+{EqMl`$1b$?s zT)>Wnh@X!S@kO~*S0Jd@l!m;cH7tzez>6IK+&w%XK!RmbzHCWV6+Hmtsi%;rsi|47 z^(XSW1%gXLcQ1c$X<>i$Q|t|x=Lkt<@Kkl}2a{#+(E@=4Ymp8;^uP~NXkSLfM76&wQO zbdkfKxMXM^|eNC6rRNi_&RS-w4LqFPS4dj z%9-D&#Bj@5S@DzoyaMg~N=FANoVc>-^oAI3Py%!}2(KEOq5GI<;&wf9or{L}33^AI z;@FG*#HNs6MIl9M==NGn#0Vq3`o$#!z)Nm3D+VAK#qyF*|Yo( zl#@((0>>wT!;XkjzbbR3fB&v(1D;R+?Rrf;J#qjT7#IKmSd`qB`d5JyGmV=&pRmr+H(( zHhtyLH>t7Yi9ly#u4U#v)^Jv=zL>Py#)R_=N}0<~9_GYC^z7L)p+r?-I6e&@htVIb z%o9#(xgj{)a6wVgGhzsU(rfqxNrcYU*qV>!s`bg;Q;;wYxAqymkvy5)_3kUzef27y z25Pi63HOmqS}r`IeliD);aX)16c&Bu znq%jvkPov6GCFzG``5}?iFMF1p#TSGQf_GRHYYKpuNK1b~o3 zy~$MoNL@cv=7OSy*6Ke35GqHH)NX)-0|+_Vy`3d=iRdX8_$r4WwM`N3i?YIbb7YyWDhys)R77f0mcWsh&c+7r^F7fGadmaw z(xxU>NH)NDf6sPSY^EV(6O$J8RhIV6VWGG zIi7AXk#JsGuPuUW-G0CqYKw@#hi+Ole$GdBAWQslX}E_wI`- zw?ra-k}L{hnh6DCg9JV#*0LlZj)~&>%NY>>>%RK0&-nE8ba-)bNOjEg>({U89%s%8 z`{ERW|NZyAc-ukk*m2MeNwqP+G5h|dCPA%r0`);HF(oC8ov$9Tq0`El?^~>pc{9WrF#j?5^#(zk(s3Fu}sDNvl4#z=i0Ofd}i$oJG2$xkP1${m9{cA%; zLU46CIXT9LhN#)<|HRkP_`XJr-)m}K9&uhin5IP#g3%Wg=Jjf z0T)X%|5nEd#gnYyD1hA1dZI?M5i#;~_Ry@W$L5V~*Q-WHF*15HXyU_D(Ps0L>F9(e7CaXzzAc6W(imv}v` zL}Q{xARg$iwwV|n1};%-(*4~M<{%DH9lHSy`H_)ONkJV=36MZgrk-&8$wYky!XP6A zXlQHTDVcEyEZ5R+AFGPvy=?xMxBWuuB-RTTnSy+cy4e9L66DZvt3Atm62_o5Dr(kl zIU4Z8FgNmxNqnJ}bvM3=eK$5LlBl&Z<15euK(`v7oXip>N{E1Nu*M0zhJwlCmm2

  • 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 z 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

    &?@X6BDEJE2N3?2 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