From 3fc7024df9b5e522185d3fbc81f93c8312c37315 Mon Sep 17 00:00:00 2001 From: libretroadmin Date: Sun, 16 Jun 2024 22:20:15 +0200 Subject: [PATCH] (cheevos) Cleanups - only use snprintf for formatting --- cheevos/cheevos.c | 80 +- frontend/drivers/platform_ctr.c | 12 +- frontend/drivers/platform_emscripten.c | 10 +- frontend/drivers/platform_win32.c | 20 +- frontend/drivers/platform_xdk.c | 2 +- record/record_driver.c | 1034 ++++++++++++------------ retroarch.c | 2 +- 7 files changed, 592 insertions(+), 568 deletions(-) diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 097b4708ec..745e45001e 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -398,15 +398,15 @@ static void rcheevos_show_mastery_placard(void) #if defined (HAVE_GFX_WIDGETS) if (gfx_widgets_ready()) { + char msg[128]; + char badge_name[32]; const char* displayname = rc_client_get_user_info(rcheevos_locals.client)->display_name; const bool content_runtime_log = settings->bools.content_runtime_log; const bool content_runtime_log_aggr = settings->bools.content_runtime_log_aggregate; - char badge_name[32]; - char msg[128]; size_t len = strlcpy(msg, displayname, sizeof(msg)); - if (len < sizeof(msg) - 12 && - (content_runtime_log || content_runtime_log_aggr)) + if (len < sizeof(msg) - 12 + && (content_runtime_log || content_runtime_log_aggr)) { const char* content_path = path_get(RARCH_PATH_CONTENT); const char* core_path = path_get(RARCH_PATH_CORE); @@ -422,7 +422,7 @@ static void rcheevos_show_mastery_placard(void) runtime_log_add_runtime_usec(runtime_log, runloop_state->core_runtime_usec); - len += snprintf(msg + len, sizeof(msg) - len, " | "); + len += strlcpy(msg + len, " | ", sizeof(msg) - len); runtime_log_get_runtime_str(runtime_log, msg + len, sizeof(msg) - len); msg[sizeof(msg) - 1] = '\0'; @@ -430,7 +430,8 @@ static void rcheevos_show_mastery_placard(void) } } - snprintf(badge_name, sizeof(badge_name), "i%s", game->badge_name); + len = strlcpy(badge_name, "i", sizeof(badge_name)); + strlcpy(badge_name + len, game->badge_name, sizeof(badge_name) - len); gfx_widgets_push_achievement(title, msg, badge_name); } else @@ -474,8 +475,10 @@ static void rcheevos_award_achievement(const rc_client_achievement_t* cheevo) #endif { char buffer[256]; - snprintf(buffer, sizeof(buffer), "%s: %s", - msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), cheevo->title); + size_t _len = strlcpy(buffer, msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), + sizeof(buffer)); + _len += strlcpy(buffer + _len, ": ", sizeof(buffer) - _len); + strlcpy(buffer + _len, cheevo->title, sizeof(buffer) - _len); runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); runloop_msg_queue_push(cheevo->description, 0, 3 * 60, false, NULL, @@ -558,12 +561,13 @@ static void rcheevos_lboard_started(const rc_client_leaderboard_t* lboard) if (settings->bools.cheevos_visibility_lboard_start) { char buffer[256]; + size_t _len = strlcpy(buffer, msg_hash_to_str(MSG_LEADERBOARD_STARTED), + sizeof(buffer)); + _len += strlcpy(buffer + _len, ": ", sizeof(buffer) - _len); + _len += strlcpy(buffer + _len, lboard->title, sizeof(buffer) - _len); if (lboard->description && *lboard->description) - snprintf(buffer, sizeof(buffer), "%s: %s - %s", - msg_hash_to_str(MSG_LEADERBOARD_STARTED), lboard->title, lboard->description); - else - snprintf(buffer, sizeof(buffer), "%s: %s", - msg_hash_to_str(MSG_LEADERBOARD_STARTED), lboard->title); + snprintf(buffer + _len, sizeof(buffer) - _len, "- %s", + lboard->description); runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); @@ -623,8 +627,9 @@ static void rcheevos_client_log_message(const char* message, const rc_client_t* static void rcheevos_server_error(const char* api_name, const char* message) { char buffer[256]; - snprintf(buffer, sizeof(buffer), "%s failed: %s", api_name, message); - + size_t _len = strlcpy(buffer, api_name, sizeof(buffer)); + _len += strlcpy(buffer + _len, " failed: ", sizeof(buffer) - _len); + _len += strlcpy(buffer + _len, message, sizeof(buffer) - _len); runloop_msg_queue_push(buffer, 0, 4 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR); } @@ -786,8 +791,10 @@ void rcheevos_award_achievement(rcheevos_locals_t* locals, #endif { char buffer[256]; - snprintf(buffer, sizeof(buffer), "%s: %s", - msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), cheevo->title); + size_t _len = strlcpy(buffer, msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), + sizeof(buffer)); + _len += strlcpy(buffer + _len, ": ", sizeof(buffer) - _len); + _len += strlcpy(buffer + _len, cheevo->title, sizeof(buffer) - _len); runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); runloop_msg_queue_push(cheevo->description, 0, 3 * 60, false, NULL, @@ -867,7 +874,8 @@ static void rcheevos_assign_leaderboard_tracker_ids(rcheevos_locals_t* locals) unsigned tracker_id; char buffer[32]; - for (; lboard < end; ++lboard) { + for (; lboard < end; ++lboard) + { if (lboard->active_tracker_id != 0xFF) continue; @@ -875,7 +883,8 @@ static void rcheevos_assign_leaderboard_tracker_ids(rcheevos_locals_t* locals) if (locals->active_lboard_trackers != 0 && lboard->value_hash != 0) { scan = locals->game.leaderboards; - for (; scan < end; ++scan) { + for (; scan < end; ++scan) + { if (scan->active_tracker_id == 0 || scan->active_tracker_id == 0xFF) continue; @@ -983,7 +992,6 @@ static void rcheevos_lboard_canceled(rcheevos_ralboard_t * lboard, bool widgets_ready) { const settings_t *settings = config_get_ptr(); - char buffer[256]; if (!lboard) return; @@ -997,8 +1005,11 @@ static void rcheevos_lboard_canceled(rcheevos_ralboard_t * lboard, if (settings->bools.cheevos_visibility_lboard_cancel) { - snprintf(buffer, sizeof(buffer), "%s: %s", - msg_hash_to_str(MSG_LEADERBOARD_FAILED), lboard->title); + char buffer[256]; + size_t _len = strlcpy(buffer, msg_hash_to_str(MSG_LEADERBOARD_FAILED), + sizeof(buffer)); + _len += strlcpy(buffer + _len, ": ", sizeof(buffer) - _len); + strlcpy(buffer + _len, lboard->title, sizeof(buffer) - _len); runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } @@ -1032,12 +1043,13 @@ static void rcheevos_lboard_started( if (settings->bools.cheevos_visibility_lboard_start) { + size_t _len = strlcpy(buffer, msg_hash_to_str(MSG_LEADERBOARD_STARTED), + sizeof(buffer)); + _len += strlcpy(buffer + _len, ": ", sizeof(buffer) - _len); + _len += strlcpy(buffer + _len, lboard->title, sizeof(buffer) - _len); if (lboard->description && *lboard->description) - snprintf(buffer, sizeof(buffer), "%s: %s - %s", - msg_hash_to_str(MSG_LEADERBOARD_STARTED), lboard->title, lboard->description); - else - snprintf(buffer, sizeof(buffer), "%s: %s", - msg_hash_to_str(MSG_LEADERBOARD_STARTED), lboard->title); + snprintf(buffer + _len, sizeof(buffer) - _len, "- %s", + lboard->description); runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); @@ -1782,6 +1794,7 @@ void rcheevos_validate_config_settings(void) if (!rc_libretro_is_setting_allowed(disallowed_settings, key, val)) { char buffer[128]; + /* TODO/FIXME - localize */ snprintf(buffer, sizeof(buffer), "Hardcore paused. Setting not allowed: %s=%s", key, val); CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", buffer); rcheevos_pause_hardcore(); @@ -2344,7 +2357,8 @@ static void rcheevos_show_game_placard(void) if (gfx_widgets_ready()) { char badge_name[32]; - snprintf(badge_name, sizeof(badge_name), "i%s", game->badge_name); + size_t _len = strlcpy(badge_name, "i", sizeof(badge_name)); + _len += strlcpy(badge_name + _len, game->badge_name, sizeof(badge_name) - _len); gfx_widgets_push_achievement(game->title, msg, badge_name); } else @@ -2407,12 +2421,13 @@ static void rcheevos_client_login_callback(int result, const char* error_message, rc_client_t* client, void* userdata) { const rc_client_user_t* user; - char msg[256] = ""; if (result != RC_OK) { - snprintf(msg, sizeof(msg), "RetroAchievements login failed: %s", - error_message); + char msg[256]; + size_t _len = strlcpy(msg, "RetroAchievements login failed: ", + sizeof(msg)); + _len += strlcpy(msg + _len, error_message, sizeof(msg) - _len); CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", msg); runloop_msg_queue_push(msg, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); @@ -2444,6 +2459,7 @@ static void rcheevos_client_login_callback(int result, if (settings->bools.cheevos_visibility_account) { /* TODO/FIXME - localize */ + char msg[256]; snprintf(msg, sizeof(msg), "RetroAchievements: Logged in as \"%s\".", user->display_name); @@ -2595,9 +2611,9 @@ void rcheevos_show_mastery_placard(void) #if defined (HAVE_GFX_WIDGETS) if (gfx_widgets_ready()) { + char msg[128]; const bool content_runtime_log = settings->bools.content_runtime_log; const bool content_runtime_log_aggr = settings->bools.content_runtime_log_aggregate; - char msg[128]; size_t len = strlcpy(msg, rcheevos_locals.displayname, sizeof(msg)); if (len < sizeof(msg) - 12 && diff --git a/frontend/drivers/platform_ctr.c b/frontend/drivers/platform_ctr.c index ee4841cf43..cb37b99b27 100644 --- a/frontend/drivers/platform_ctr.c +++ b/frontend/drivers/platform_ctr.c @@ -79,7 +79,7 @@ static void get_first_valid_core(char* path_return, size_t len) { if (!ent) break; - if (strlen(ent->d_name) > strlen(extension) + if (strlen(ent->d_name) > strlen(extension) && !strcmp(ent->d_name + strlen(ent->d_name) - strlen(extension), extension)) { size_t _len = strlcpy(path_return, "sdmc:/retroarch/cores/", len); @@ -244,8 +244,8 @@ static void frontend_ctr_exec(const char *path, bool should_load_game) is corrupt so we have to quit */ { char error[PATH_MAX + 32]; - - snprintf(error, sizeof(error), "Can't launch core: %s", path); + size_t _len = strlcpy(error, "Can't launch core: ", sizeof(error)); + strlcpy(error + _len, path, sizeof(error) - _len); error_and_quit(error); } } @@ -335,7 +335,7 @@ static void ctr_check_dspfirm(void) { size_t dspfirm_size = ptr[1]; ptr -= 0x40; - if ((ptr + (dspfirm_size >> 2)) > + if ((ptr + (dspfirm_size >> 2)) > (code_buffer + (code_size >> 2))) break; @@ -480,7 +480,7 @@ static void frontend_ctr_init(void* data) static int frontend_ctr_get_rating(void) { u8 device_model = 0xFF; - + /*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/ CFGU_GetSystemModel(&device_model); @@ -592,7 +592,7 @@ static void frontend_ctr_get_os(char* s, size_t len, int* major, int* minor) static void frontend_ctr_get_name(char* s, size_t len) { u8 device_model = 0xFF; - + /*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/ CFGU_GetSystemModel(&device_model); diff --git a/frontend/drivers/platform_emscripten.c b/frontend/drivers/platform_emscripten.c index 3856bdf08b..5c0511e35d 100644 --- a/frontend/drivers/platform_emscripten.c +++ b/frontend/drivers/platform_emscripten.c @@ -79,12 +79,10 @@ static void frontend_emscripten_get_env(int *argc, char *argv[], if (home) { - base_path[0] = '\0'; - user_path[0] = '\0'; - snprintf(base_path, sizeof(base_path), - "%s/retroarch", home); - snprintf(user_path, sizeof(user_path), - "%s/retroarch/userdata", home); + size_t _len = strlcpy(base_path, home, sizeof(base_path)); + strlcpy(base_path + _len, "/retroarch", sizeof(base_path) - _len); + _len = strlcpy(user_path, home, sizeof(user_path)); + strlcpy(user_path + _len, "/retroarch/userdata", sizeof(user_path) - _len); } else { diff --git a/frontend/drivers/platform_win32.c b/frontend/drivers/platform_win32.c index 5a4487fa38..47e7ebd421 100644 --- a/frontend/drivers/platform_win32.c +++ b/frontend/drivers/platform_win32.c @@ -453,7 +453,7 @@ static void frontend_win32_init(void *data) static void init_nvda(void) { #ifdef HAVE_DYLIB - if ( (g_plat_win32_flags & PLAT_WIN32_FLAG_USE_NVDA) + if ( (g_plat_win32_flags & PLAT_WIN32_FLAG_USE_NVDA) && !nvda_lib) { if ((nvda_lib = dylib_load("nvdaControllerClient64.dll"))) @@ -750,8 +750,8 @@ static void frontend_win32_respawn(char *s, size_t len, char *args) { STARTUPINFO si; PROCESS_INFORMATION pi; + char executable_args[PATH_MAX_LENGTH]; char executable_path[PATH_MAX_LENGTH] = {0}; - char executable_args[PATH_MAX_LENGTH] = {0}; if (win32_fork_mode != FRONTEND_FORK_RESTART) return; @@ -761,7 +761,7 @@ static void frontend_win32_respawn(char *s, size_t len, char *args) path_set(RARCH_PATH_CORE, executable_path); /* Remove executable path from arguments given to CreateProcess */ - snprintf(executable_args, sizeof(executable_args), "%s", strstr(args, ".exe") + 4); + strlcpy(executable_args, strstr(args, ".exe") + 4, sizeof(executable_args)); memset(&si, 0, sizeof(si)); si.cb = sizeof(si); @@ -853,7 +853,7 @@ static const char *accessibility_win_language_id(const char* language) return "412"; else if (string_is_equal(language,"pl")) return "415"; - else if (string_is_equal(language,"cs")) + else if (string_is_equal(language,"cs")) return "405"; return ""; } @@ -966,12 +966,12 @@ static bool create_win32_process(char* cmd, const char * input) size_t input_len = strlen(input); if (!CreatePipe(&rd, &wr, NULL, input_len)) return false; - + SetHandleInformation(rd, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); - + WriteFile(wr, input, input_len, &dummy, NULL); CloseHandle(wr); - + si.dwFlags |= STARTF_USESTDHANDLES; si.hStdInput = rd; si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); @@ -1006,7 +1006,7 @@ static bool is_narrator_running_windows(void) { long res = nvdaController_testIfRunning_func(); - if (res != 0) + if (res != 0) { /* The running nvda service wasn't found, so revert back to the powershell method @@ -1057,7 +1057,7 @@ static bool accessibility_speak_windows(int speed, #ifdef HAVE_NVDA init_nvda(); #endif - + if (g_plat_win32_flags & PLAT_WIN32_FLAG_USE_POWERSHELL) { const char * template_lang = "powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.SelectVoice(\\\"%s\\\"); $synth.Rate = %s; $synth.Speak($input);\""; @@ -1079,7 +1079,7 @@ static bool accessibility_speak_windows(int speed, wchar_t *wc = utf8_to_utf16_string_alloc(speak_text); long res = nvdaController_testIfRunning_func(); - if (!wc || res != 0) + if (!wc || res != 0) { RARCH_ERR("Error communicating with NVDA\n"); if (wc) diff --git a/frontend/drivers/platform_xdk.c b/frontend/drivers/platform_xdk.c index fbe04b3b3e..3ccfb75f6d 100644 --- a/frontend/drivers/platform_xdk.c +++ b/frontend/drivers/platform_xdk.c @@ -269,7 +269,7 @@ static void frontend_xdk_exec(const char *path, bool should_load_content) memset(&ptr, 0, sizeof(ptr)); if (should_load_content && !path_is_empty(RARCH_PATH_CONTENT)) - snprintf((char*)ptr.Data, sizeof(ptr.Data), "%s", path_get(RARCH_PATH_CONTENT)); + strlcpy((char*)ptr.Data, path_get(RARCH_PATH_CONTENT), sizeof(ptr.Data)); if (!string_is_empty(path)) XLaunchNewImage(path, !string_is_empty((const char*)ptr.Data) ? &ptr : NULL); diff --git a/record/record_driver.c b/record/record_driver.c index 89491665ad..5270ca2607 100644 --- a/record/record_driver.c +++ b/record/record_driver.c @@ -1,512 +1,522 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2021 - Daniel De Matteis - * Copyright (C) 2016-2019 - 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 . - */ - -#include -#include -#include -#include -#include - -#include "../configuration.h" -#include "../list_special.h" -#include "../gfx/video_driver.h" -#include "../paths.h" -#include "../retroarch.h" -#include "../runloop.h" -#include "../verbosity.h" -#include "../defaults.h" - -#include "record_driver.h" - -static recording_state_t recording_state = {0}; - -static const record_driver_t record_null = { - NULL, /* new */ - NULL, /* free */ - NULL, /* push_video */ - NULL, /* push_audio */ - NULL, /* finalize */ - "null", -}; - -const record_driver_t *record_drivers[] = { -#ifdef HAVE_FFMPEG - &record_ffmpeg, -#endif - &record_null, - NULL, -}; - -recording_state_t *recording_state_get_ptr(void) -{ - return &recording_state; -} - -/** - * config_get_record_driver_options: - * - * Get an enumerated list of all record driver names, separated by '|'. - * - * @return string listing of all record driver names, separated by '|'. - **/ -const char* config_get_record_driver_options(void) -{ - return char_list_new_special(STRING_LIST_RECORD_DRIVERS, NULL); -} - -#if 0 -/* TODO/FIXME - not used apparently */ -static void find_record_driver(const char *prefix, - bool verbosity_enabled) -{ - settings_t *settings = config_get_ptr(); - int i = (int)driver_find_index( - "record_driver", - settings->arrays.record_driver); - - if (i >= 0) - recording_state.driver = (const record_driver_t*)record_drivers[i]; - else - { - if (verbosity_enabled) - { - unsigned d; - - RARCH_ERR("[Recording]: Couldn't find any %s named \"%s\".\n", prefix, - settings->arrays.record_driver); - RARCH_LOG_OUTPUT("Available %ss are:\n", prefix); - for (d = 0; record_drivers[d]; d++) - RARCH_LOG_OUTPUT("\t%s\n", record_drivers[d].ident); - RARCH_WARN("[Recording]: Going to default to first %s...\n", prefix); - } - - recording_state.driver = (const record_driver_t*)record_drivers[0]; - - if (!recording_state.driver) - retroarch_fail(1, "find_record_driver()"); - } -} - -/** - * ffemu_find_backend: - * @ident : Identifier of driver to find. - * - * Finds a recording driver with the name @ident. - * - * Returns: recording driver handle if successful, otherwise - * NULL. - **/ -static const record_driver_t *ffemu_find_backend(const char *ident) -{ - unsigned i; - - for (i = 0; record_drivers[i]; i++) - { - if (string_is_equal(record_drivers[i]->ident, ident)) - return record_drivers[i]; - } - - return NULL; -} - -static void recording_driver_free_state(void) -{ - /* TODO/FIXME - this is not being called anywhere */ - recording_state.gpu_width = 0; - recording_state.gpu_height = 0; - recording_state.width = 0; - recording_stte.height = 0; -} -#endif - -/** - * gfx_ctx_init_first: - * @param backend - * Recording backend handle. - * @param data - * Recording data handle. - * @param params - * Recording info parameters. - * - * Finds first suitable recording context driver and initializes. - * - * @return true if successful, otherwise false. - **/ -static bool record_driver_init_first( - const record_driver_t **backend, void **data, - const struct record_params *params) -{ - unsigned i; - - for (i = 0; record_drivers[i]; i++) - { - void *handle = NULL; - if (!record_drivers[i]->init) - continue; - if (!(handle = record_drivers[i]->init(params))) - continue; - - *backend = record_drivers[i]; - *data = handle; - return true; - } - - return false; -} - -bool recording_deinit(void) -{ - recording_state_t *recording_st = &recording_state; -#ifdef HAVE_FFMPEG - settings_t *settings = config_get_ptr(); - bool history_list_enable = settings->bools.history_list_enable; -#endif - - if ( !recording_st->data - || !recording_st->driver) - return false; - - if (recording_st->driver->finalize) - recording_st->driver->finalize(recording_st->data); - - if (recording_st->driver->free) - recording_st->driver->free(recording_st->data); - - recording_st->data = NULL; - recording_st->driver = NULL; - - video_driver_gpu_record_deinit(); - - /* Push recording to video history playlist */ -#ifdef HAVE_FFMPEG - if ( history_list_enable - && !string_is_empty(recording_st->path)) - { - struct playlist_entry entry = {0}; - - /* the push function reads our entry as const, so these casts are safe */ - entry.path = recording_st->path; - entry.core_path = (char*)"builtin"; - entry.core_name = (char*)"movieplayer"; - - command_playlist_push_write(g_defaults.video_history, &entry); - } -#endif - - /* Forget cached path to create a new one next */ - recording_st->path[0] = '\0'; - - return true; -} - -void streaming_set_state(bool state) -{ - recording_state_t *recording_st = &recording_state; - recording_st->streaming_enable = state; -} - -bool recording_init(void) -{ - char output[PATH_MAX_LENGTH]; - char buf[PATH_MAX_LENGTH]; - struct record_params params = {0}; - settings_t *settings = config_get_ptr(); - video_driver_state_t *video_st = video_state_get_ptr(); - struct retro_system_av_info *av_info = &video_st->av_info; - runloop_state_t *runloop_st = runloop_state_get_ptr(); - bool video_gpu_record = settings->bools.video_gpu_record; - bool video_force_aspect = settings->bools.video_force_aspect; - const enum rarch_core_type - current_core_type = runloop_st->current_core_type; - const enum retro_pixel_format - video_driver_pix_fmt = video_st->pix_fmt; - recording_state_t *recording_st = &recording_state; - bool recording_enable = recording_st->enable; - - if (!recording_enable) - return false; - - output[0] = '\0'; - - if (current_core_type == CORE_TYPE_DUMMY) - { - RARCH_WARN("[Recording]: %s\n", - msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED)); - return false; - } - - if (!video_gpu_record && video_driver_is_hw_context()) - { - RARCH_WARN("[Recording]: %s.\n", - msg_hash_to_str(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING)); - return false; - } - - RARCH_LOG("[Recording]: %s: FPS: %.2f, Sample rate: %.2f\n", - msg_hash_to_str(MSG_CUSTOM_TIMING_GIVEN), - (float)av_info->timing.fps, - (float)av_info->timing.sample_rate); - - if (!string_is_empty(recording_st->path)) - strlcpy(output, recording_st->path, sizeof(output)); - else - { - const char *stream_url = settings->paths.path_stream_url; - unsigned video_record_quality = settings->uints.video_record_quality; - unsigned video_stream_port = settings->uints.video_stream_port; - if (recording_st->streaming_enable) - if (!string_is_empty(stream_url)) - strlcpy(output, stream_url, sizeof(output)); - else - /* Fallback, stream locally to 127.0.0.1 */ - snprintf(output, sizeof(output), "udp://127.0.0.1:%u", - video_stream_port); - else - { - const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); - /* Fallback to core name if started without content */ - if (string_is_empty(game_name)) - game_name = runloop_st->system.info.library_name; - - if (video_record_quality < RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST) - { - fill_str_dated_filename(buf, game_name, - "mkv", sizeof(buf)); - fill_pathname_join_special(output, recording_st->output_dir, buf, sizeof(output)); - } - else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST - && video_record_quality < RECORD_CONFIG_TYPE_RECORDING_GIF) - { - fill_str_dated_filename(buf, game_name, - "webm", sizeof(buf)); - fill_pathname_join_special(output, recording_st->output_dir, buf, sizeof(output)); - } - else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_GIF - && video_record_quality < RECORD_CONFIG_TYPE_RECORDING_APNG) - { - fill_str_dated_filename(buf, game_name, - "gif", sizeof(buf)); - fill_pathname_join_special(output, recording_st->output_dir, buf, sizeof(output)); - } - else - { - fill_str_dated_filename(buf, game_name, - "png", sizeof(buf)); - fill_pathname_join_special(output, recording_st->output_dir, buf, sizeof(output)); - } - - /* Cache path for playlist saving */ - if (!string_is_empty(output)) - strlcpy(recording_st->path, output, sizeof(recording_st->path)); - } - } - - params.audio_resampler = settings->arrays.audio_resampler; - params.video_gpu_record = settings->bools.video_gpu_record; - params.video_record_scale_factor = settings->uints.video_record_scale_factor; - params.video_stream_scale_factor = settings->uints.video_stream_scale_factor; - params.video_record_threads = settings->uints.video_record_threads; - params.streaming_mode = settings->uints.streaming_mode; - - params.out_width = av_info->geometry.base_width; - params.out_height = av_info->geometry.base_height; - params.fb_width = av_info->geometry.max_width; - params.fb_height = av_info->geometry.max_height; - params.channels = 2; - params.filename = output; - params.fps = av_info->timing.fps; - params.samplerate = av_info->timing.sample_rate; - params.pix_fmt = - (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888) - ? FFEMU_PIX_ARGB8888 - : FFEMU_PIX_RGB565; - params.config = NULL; - - if (!string_is_empty(recording_st->config)) - params.config = recording_st->config; - else - { - if (recording_st->streaming_enable) - { - params.config = settings->paths.path_stream_config; - params.preset = (enum record_config_type) - settings->uints.video_stream_quality; - } - else - { - params.config = settings->paths.path_record_config; - params.preset = (enum record_config_type) - settings->uints.video_record_quality; - } - } - - if (settings->bools.video_gpu_record - && video_st->current_video->read_viewport) - { - unsigned gpu_size; - struct video_viewport vp; - - vp.x = 0; - vp.y = 0; - vp.width = 0; - vp.height = 0; - vp.full_width = 0; - vp.full_height = 0; - - video_driver_get_viewport_info(&vp); - - if (!vp.width || !vp.height) - { - RARCH_ERR("[Recording]: Failed to get viewport information from video driver. " - "Cannot start recording ...\n"); - return false; - } - - params.out_width = vp.width; - params.out_height = vp.height; - params.fb_width = next_pow2(vp.width); - params.fb_height = next_pow2(vp.height); - - if (video_force_aspect && - (video_st->aspect_ratio > 0.0f)) - params.aspect_ratio = video_st->aspect_ratio; - else - params.aspect_ratio = (float)vp.width / vp.height; - - params.pix_fmt = FFEMU_PIX_BGR24; - recording_st->gpu_width = vp.width; - recording_st->gpu_height = vp.height; - - RARCH_LOG("[Recording]: %s %ux%u.\n", msg_hash_to_str(MSG_DETECTED_VIEWPORT_OF), - vp.width, vp.height); - - gpu_size = vp.width * vp.height * 3; - if (!(video_st->record_gpu_buffer = (uint8_t*)malloc(gpu_size))) - return false; - } - else - { - if (recording_state.width || recording_state.height) - { - params.out_width = recording_state.width; - params.out_height = recording_state.height; - } - - if (video_force_aspect && - (video_st->aspect_ratio > 0.0f)) - params.aspect_ratio = video_st->aspect_ratio; - else - params.aspect_ratio = (float)params.out_width / params.out_height; - -#ifdef HAVE_VIDEO_FILTER - if (settings->bools.video_post_filter_record - && !!video_st->state_filter) - { - unsigned max_width = 0; - unsigned max_height = 0; - - params.pix_fmt = FFEMU_PIX_RGB565; - - if (video_st->flags & VIDEO_FLAG_STATE_OUT_RGB32) - params.pix_fmt = FFEMU_PIX_ARGB8888; - - rarch_softfilter_get_max_output_size( - video_st->state_filter, - &max_width, &max_height); - params.fb_width = next_pow2(max_width); - params.fb_height = next_pow2(max_height); - } -#endif - } - - RARCH_LOG("[Recording]: %s %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n", - msg_hash_to_str(MSG_RECORDING_TO), - output, - params.out_width, params.out_height, - params.fb_width, params.fb_height, - (unsigned)params.pix_fmt); - - if (!record_driver_init_first( - &recording_state.driver, - &recording_state.data, ¶ms)) - { - RARCH_ERR("[Recording]: %s\n", - msg_hash_to_str(MSG_FAILED_TO_START_RECORDING)); - video_driver_gpu_record_deinit(); - - return false; - } - - return true; -} - -void recording_driver_update_streaming_url(void) -{ - settings_t *settings = config_get_ptr(); - const char *youtube_url = "rtmp://a.rtmp.youtube.com/live2/"; - const char *twitch_url = "rtmp://live.twitch.tv/app/"; - const char *facebook_url = "rtmps://live-api-s.facebook.com:443/rtmp/"; - - if (!settings) - return; - - switch (settings->uints.streaming_mode) - { - case STREAMING_MODE_TWITCH: - if (!string_is_empty(settings->arrays.twitch_stream_key)) - { - size_t _len = strlcpy(settings->paths.path_stream_url, - twitch_url, - sizeof(settings->paths.path_stream_url)); - strlcpy(settings->paths.path_stream_url + _len, - settings->arrays.twitch_stream_key, - sizeof(settings->paths.path_stream_url) - _len); - } - break; - case STREAMING_MODE_YOUTUBE: - if (!string_is_empty(settings->arrays.youtube_stream_key)) - { - size_t _len = strlcpy(settings->paths.path_stream_url, - youtube_url, - sizeof(settings->paths.path_stream_url)); - strlcpy(settings->paths.path_stream_url + _len, - settings->arrays.youtube_stream_key, - sizeof(settings->paths.path_stream_url) - _len); - } - break; - case STREAMING_MODE_LOCAL: - /* TODO: figure out default interface and bind to that instead */ - snprintf(settings->paths.path_stream_url, sizeof(settings->paths.path_stream_url), - "udp://%s:%u", "127.0.0.1", settings->uints.video_stream_port); - break; - case STREAMING_MODE_CUSTOM: - default: - /* Do nothing, let the user input the URL */ - break; - case STREAMING_MODE_FACEBOOK: - if (!string_is_empty(settings->arrays.facebook_stream_key)) - { - size_t _len = strlcpy(settings->paths.path_stream_url, - facebook_url, - sizeof(settings->paths.path_stream_url)); - strlcpy(settings->paths.path_stream_url + _len, - settings->arrays.facebook_stream_key, - sizeof(settings->paths.path_stream_url) - _len); - } - break; - } -} +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2021 - Daniel De Matteis + * Copyright (C) 2016-2019 - 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 . + */ + +#include +#include +#include +#include +#include + +#include "../configuration.h" +#include "../list_special.h" +#include "../gfx/video_driver.h" +#include "../paths.h" +#include "../retroarch.h" +#include "../runloop.h" +#include "../verbosity.h" +#include "../defaults.h" + +#include "record_driver.h" + +static recording_state_t recording_state = {0}; + +static const record_driver_t record_null = { + NULL, /* new */ + NULL, /* free */ + NULL, /* push_video */ + NULL, /* push_audio */ + NULL, /* finalize */ + "null", +}; + +const record_driver_t *record_drivers[] = { +#ifdef HAVE_FFMPEG + &record_ffmpeg, +#endif + &record_null, + NULL, +}; + +recording_state_t *recording_state_get_ptr(void) +{ + return &recording_state; +} + +/** + * config_get_record_driver_options: + * + * Get an enumerated list of all record driver names, separated by '|'. + * + * @return string listing of all record driver names, separated by '|'. + **/ +const char* config_get_record_driver_options(void) +{ + return char_list_new_special(STRING_LIST_RECORD_DRIVERS, NULL); +} + +#if 0 +/* TODO/FIXME - not used apparently */ +static void find_record_driver(const char *prefix, + bool verbosity_enabled) +{ + settings_t *settings = config_get_ptr(); + int i = (int)driver_find_index( + "record_driver", + settings->arrays.record_driver); + + if (i >= 0) + recording_state.driver = (const record_driver_t*)record_drivers[i]; + else + { + if (verbosity_enabled) + { + unsigned d; + + RARCH_ERR("[Recording]: Couldn't find any %s named \"%s\".\n", prefix, + settings->arrays.record_driver); + RARCH_LOG_OUTPUT("Available %ss are:\n", prefix); + for (d = 0; record_drivers[d]; d++) + RARCH_LOG_OUTPUT("\t%s\n", record_drivers[d].ident); + RARCH_WARN("[Recording]: Going to default to first %s...\n", prefix); + } + + recording_state.driver = (const record_driver_t*)record_drivers[0]; + + if (!recording_state.driver) + retroarch_fail(1, "find_record_driver()"); + } +} + +/** + * ffemu_find_backend: + * @ident : Identifier of driver to find. + * + * Finds a recording driver with the name @ident. + * + * Returns: recording driver handle if successful, otherwise + * NULL. + **/ +static const record_driver_t *ffemu_find_backend(const char *ident) +{ + unsigned i; + + for (i = 0; record_drivers[i]; i++) + { + if (string_is_equal(record_drivers[i]->ident, ident)) + return record_drivers[i]; + } + + return NULL; +} + +static void recording_driver_free_state(void) +{ + /* TODO/FIXME - this is not being called anywhere */ + recording_state.gpu_width = 0; + recording_state.gpu_height = 0; + recording_state.width = 0; + recording_stte.height = 0; +} +#endif + +/** + * gfx_ctx_init_first: + * @param backend + * Recording backend handle. + * @param data + * Recording data handle. + * @param params + * Recording info parameters. + * + * Finds first suitable recording context driver and initializes. + * + * @return true if successful, otherwise false. + **/ +static bool record_driver_init_first( + const record_driver_t **backend, void **data, + const struct record_params *params) +{ + unsigned i; + + for (i = 0; record_drivers[i]; i++) + { + void *handle = NULL; + if (!record_drivers[i]->init) + continue; + if (!(handle = record_drivers[i]->init(params))) + continue; + + *backend = record_drivers[i]; + *data = handle; + return true; + } + + return false; +} + +bool recording_deinit(void) +{ + recording_state_t *recording_st = &recording_state; +#ifdef HAVE_FFMPEG + settings_t *settings = config_get_ptr(); + bool history_list_enable = settings->bools.history_list_enable; +#endif + + if ( !recording_st->data + || !recording_st->driver) + return false; + + if (recording_st->driver->finalize) + recording_st->driver->finalize(recording_st->data); + + if (recording_st->driver->free) + recording_st->driver->free(recording_st->data); + + recording_st->data = NULL; + recording_st->driver = NULL; + + video_driver_gpu_record_deinit(); + + /* Push recording to video history playlist */ +#ifdef HAVE_FFMPEG + if ( history_list_enable + && !string_is_empty(recording_st->path)) + { + struct playlist_entry entry = {0}; + + /* the push function reads our entry as const, so these casts are safe */ + entry.path = recording_st->path; + entry.core_path = (char*)"builtin"; + entry.core_name = (char*)"movieplayer"; + + command_playlist_push_write(g_defaults.video_history, &entry); + } +#endif + + /* Forget cached path to create a new one next */ + recording_st->path[0] = '\0'; + + return true; +} + +void streaming_set_state(bool state) +{ + recording_state_t *recording_st = &recording_state; + recording_st->streaming_enable = state; +} + +bool recording_init(void) +{ + char output[PATH_MAX_LENGTH]; + char buf[PATH_MAX_LENGTH]; + struct record_params params = {0}; + settings_t *settings = config_get_ptr(); + video_driver_state_t *video_st = video_state_get_ptr(); + struct retro_system_av_info *av_info = &video_st->av_info; + runloop_state_t *runloop_st = runloop_state_get_ptr(); + bool video_gpu_record = settings->bools.video_gpu_record; + bool video_force_aspect = settings->bools.video_force_aspect; + const enum rarch_core_type + current_core_type = runloop_st->current_core_type; + const enum retro_pixel_format + video_driver_pix_fmt = video_st->pix_fmt; + recording_state_t *recording_st = &recording_state; + bool recording_enable = recording_st->enable; + + if (!recording_enable) + return false; + + output[0] = '\0'; + + if (current_core_type == CORE_TYPE_DUMMY) + { + RARCH_WARN("[Recording]: %s\n", + msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED)); + return false; + } + + if (!video_gpu_record && video_driver_is_hw_context()) + { + RARCH_WARN("[Recording]: %s.\n", + msg_hash_to_str(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING)); + return false; + } + + RARCH_LOG("[Recording]: %s: FPS: %.2f, Sample rate: %.2f\n", + msg_hash_to_str(MSG_CUSTOM_TIMING_GIVEN), + (float)av_info->timing.fps, + (float)av_info->timing.sample_rate); + + if (!string_is_empty(recording_st->path)) + strlcpy(output, recording_st->path, sizeof(output)); + else + { + const char *stream_url = settings->paths.path_stream_url; + unsigned video_record_quality = settings->uints.video_record_quality; + unsigned video_stream_port = settings->uints.video_stream_port; + if (recording_st->streaming_enable) + { + if (!string_is_empty(stream_url)) + strlcpy(output, stream_url, sizeof(output)); + else + { + /* Fallback, stream locally to 127.0.0.1 */ + size_t _len = strlcpy(output, "udp://127.0.0.1:", sizeof(output)); + snprintf(output + _len, sizeof(output) - _len, "%u", + video_stream_port); + } + } + else + { + const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); + /* Fallback to core name if started without content */ + if (string_is_empty(game_name)) + game_name = runloop_st->system.info.library_name; + + if (video_record_quality < RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST) + { + fill_str_dated_filename(buf, game_name, + "mkv", sizeof(buf)); + fill_pathname_join_special(output, recording_st->output_dir, buf, sizeof(output)); + } + else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST + && video_record_quality < RECORD_CONFIG_TYPE_RECORDING_GIF) + { + fill_str_dated_filename(buf, game_name, + "webm", sizeof(buf)); + fill_pathname_join_special(output, recording_st->output_dir, buf, sizeof(output)); + } + else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_GIF + && video_record_quality < RECORD_CONFIG_TYPE_RECORDING_APNG) + { + fill_str_dated_filename(buf, game_name, + "gif", sizeof(buf)); + fill_pathname_join_special(output, recording_st->output_dir, buf, sizeof(output)); + } + else + { + fill_str_dated_filename(buf, game_name, + "png", sizeof(buf)); + fill_pathname_join_special(output, recording_st->output_dir, buf, sizeof(output)); + } + + /* Cache path for playlist saving */ + if (!string_is_empty(output)) + strlcpy(recording_st->path, output, sizeof(recording_st->path)); + } + } + + params.audio_resampler = settings->arrays.audio_resampler; + params.video_gpu_record = settings->bools.video_gpu_record; + params.video_record_scale_factor = settings->uints.video_record_scale_factor; + params.video_stream_scale_factor = settings->uints.video_stream_scale_factor; + params.video_record_threads = settings->uints.video_record_threads; + params.streaming_mode = settings->uints.streaming_mode; + + params.out_width = av_info->geometry.base_width; + params.out_height = av_info->geometry.base_height; + params.fb_width = av_info->geometry.max_width; + params.fb_height = av_info->geometry.max_height; + params.channels = 2; + params.filename = output; + params.fps = av_info->timing.fps; + params.samplerate = av_info->timing.sample_rate; + params.pix_fmt = + (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888) + ? FFEMU_PIX_ARGB8888 + : FFEMU_PIX_RGB565; + params.config = NULL; + + if (!string_is_empty(recording_st->config)) + params.config = recording_st->config; + else + { + if (recording_st->streaming_enable) + { + params.config = settings->paths.path_stream_config; + params.preset = (enum record_config_type) + settings->uints.video_stream_quality; + } + else + { + params.config = settings->paths.path_record_config; + params.preset = (enum record_config_type) + settings->uints.video_record_quality; + } + } + + if (settings->bools.video_gpu_record + && video_st->current_video->read_viewport) + { + unsigned gpu_size; + struct video_viewport vp; + + vp.x = 0; + vp.y = 0; + vp.width = 0; + vp.height = 0; + vp.full_width = 0; + vp.full_height = 0; + + video_driver_get_viewport_info(&vp); + + if (!vp.width || !vp.height) + { + RARCH_ERR("[Recording]: Failed to get viewport information from video driver. " + "Cannot start recording ...\n"); + return false; + } + + params.out_width = vp.width; + params.out_height = vp.height; + params.fb_width = next_pow2(vp.width); + params.fb_height = next_pow2(vp.height); + + if (video_force_aspect && + (video_st->aspect_ratio > 0.0f)) + params.aspect_ratio = video_st->aspect_ratio; + else + params.aspect_ratio = (float)vp.width / vp.height; + + params.pix_fmt = FFEMU_PIX_BGR24; + recording_st->gpu_width = vp.width; + recording_st->gpu_height = vp.height; + + RARCH_LOG("[Recording]: %s %ux%u.\n", msg_hash_to_str(MSG_DETECTED_VIEWPORT_OF), + vp.width, vp.height); + + gpu_size = vp.width * vp.height * 3; + if (!(video_st->record_gpu_buffer = (uint8_t*)malloc(gpu_size))) + return false; + } + else + { + if (recording_state.width || recording_state.height) + { + params.out_width = recording_state.width; + params.out_height = recording_state.height; + } + + if (video_force_aspect && + (video_st->aspect_ratio > 0.0f)) + params.aspect_ratio = video_st->aspect_ratio; + else + params.aspect_ratio = (float)params.out_width / params.out_height; + +#ifdef HAVE_VIDEO_FILTER + if (settings->bools.video_post_filter_record + && !!video_st->state_filter) + { + unsigned max_width = 0; + unsigned max_height = 0; + + params.pix_fmt = FFEMU_PIX_RGB565; + + if (video_st->flags & VIDEO_FLAG_STATE_OUT_RGB32) + params.pix_fmt = FFEMU_PIX_ARGB8888; + + rarch_softfilter_get_max_output_size( + video_st->state_filter, + &max_width, &max_height); + params.fb_width = next_pow2(max_width); + params.fb_height = next_pow2(max_height); + } +#endif + } + + RARCH_LOG("[Recording]: %s %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n", + msg_hash_to_str(MSG_RECORDING_TO), + output, + params.out_width, params.out_height, + params.fb_width, params.fb_height, + (unsigned)params.pix_fmt); + + if (!record_driver_init_first( + &recording_state.driver, + &recording_state.data, ¶ms)) + { + RARCH_ERR("[Recording]: %s\n", + msg_hash_to_str(MSG_FAILED_TO_START_RECORDING)); + video_driver_gpu_record_deinit(); + + return false; + } + + return true; +} + +void recording_driver_update_streaming_url(void) +{ + settings_t *settings = config_get_ptr(); + const char *youtube_url = "rtmp://a.rtmp.youtube.com/live2/"; + const char *twitch_url = "rtmp://live.twitch.tv/app/"; + const char *facebook_url = "rtmps://live-api-s.facebook.com:443/rtmp/"; + + if (!settings) + return; + + switch (settings->uints.streaming_mode) + { + case STREAMING_MODE_TWITCH: + if (!string_is_empty(settings->arrays.twitch_stream_key)) + { + size_t _len = strlcpy(settings->paths.path_stream_url, + twitch_url, + sizeof(settings->paths.path_stream_url)); + strlcpy(settings->paths.path_stream_url + _len, + settings->arrays.twitch_stream_key, + sizeof(settings->paths.path_stream_url) - _len); + } + break; + case STREAMING_MODE_YOUTUBE: + if (!string_is_empty(settings->arrays.youtube_stream_key)) + { + size_t _len = strlcpy(settings->paths.path_stream_url, + youtube_url, + sizeof(settings->paths.path_stream_url)); + strlcpy(settings->paths.path_stream_url + _len, + settings->arrays.youtube_stream_key, + sizeof(settings->paths.path_stream_url) - _len); + } + break; + case STREAMING_MODE_LOCAL: + { + /* TODO: figure out default interface and bind to that instead */ + size_t _len = strlcpy(settings->paths.path_stream_url, "udp://127.0.0.1:", + sizeof(settings->paths.path_stream_url)); + snprintf(settings->paths.path_stream_url + _len, + sizeof(settings->paths.path_stream_url) - _len, + "%u", settings->uints.video_stream_port); + } + break; + case STREAMING_MODE_FACEBOOK: + if (!string_is_empty(settings->arrays.facebook_stream_key)) + { + size_t _len = strlcpy(settings->paths.path_stream_url, + facebook_url, + sizeof(settings->paths.path_stream_url)); + strlcpy(settings->paths.path_stream_url + _len, + settings->arrays.facebook_stream_key, + sizeof(settings->paths.path_stream_url) - _len); + } + break; + case STREAMING_MODE_CUSTOM: + default: + /* Do nothing, let the user input the URL */ + break; + } +} diff --git a/retroarch.c b/retroarch.c index c6e075587d..f3dec5dd32 100644 --- a/retroarch.c +++ b/retroarch.c @@ -8171,7 +8171,7 @@ int retroarch_get_capabilities(enum rarch_capabilities type, _MSC_VER); #elif defined(__SNC__) _len = strlcpy(str_out, msg_hash_to_str(MSG_COMPILER), str_len); - _len += snprintf(str_out + _len, str_len - _Len, ": SNC (%d)", + _len += snprintf(str_out + _len, str_len - _len, ": SNC (%d)", __SN_VER__); #elif defined(_WIN32) && defined(__GNUC__) _len = strlcpy(str_out, msg_hash_to_str(MSG_COMPILER), str_len);