diff --git a/cores/libretro-net-retropad/net_retropad_core.c b/cores/libretro-net-retropad/net_retropad_core.c index 4eacb44191a5..7091c83c287f 100644 --- a/cores/libretro-net-retropad/net_retropad_core.c +++ b/cores/libretro-net-retropad/net_retropad_core.c @@ -394,6 +394,8 @@ static bool input_test_file_read(const char* file_path) (int)rjson_get_source_line(parser), (int)rjson_get_source_column(parser), (*rjson_get_error(parser) ? rjson_get_error(parser) : "format error")); + if (last_test_step > MAX_TEST_STEPS) + last_test_step = 0; } /* Free parser */ @@ -412,6 +414,12 @@ static bool input_test_file_read(const char* file_path) { NETRETROPAD_CORE_PREFIX(log_cb)(RETRO_LOG_WARN,"[Remote RetroPad]: too long test input json, maximum size: %d\n",MAX_TEST_STEPS); } + if (last_test_step == 0) + { + NETRETROPAD_CORE_PREFIX(log_cb)(RETRO_LOG_WARN,"[Remote RetroPad]: no steps in input json\n"); + success = false; + } + for (current_test_step = 0; current_test_step < last_test_step; current_test_step++) { NETRETROPAD_CORE_PREFIX(log_cb)(RETRO_LOG_DEBUG, @@ -562,7 +570,14 @@ unsigned NETRETROPAD_CORE_PREFIX(retro_api_version)(void) } void NETRETROPAD_CORE_PREFIX(retro_set_controller_port_device)( - unsigned port, unsigned device) { } + unsigned port, unsigned device) +{ + const char msg[] = "Input device type change is not supported!"; + struct retro_error_message e; + e.code = RETROE_UNSUPPORTED_ACTION | ((port & 0xFF) << 8) | (device & 0xFF); + e.message = msg; + NETRETROPAD_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_ERROR_CODE, &e); +} void NETRETROPAD_CORE_PREFIX(retro_get_system_info)( struct retro_system_info *info) @@ -1385,13 +1400,16 @@ void NETRETROPAD_CORE_PREFIX(retro_run)(void) bool NETRETROPAD_CORE_PREFIX(retro_load_game)(const struct retro_game_info *info) { + bool load_result = true; + netretropad_check_variables(); open_UDP_socket(); /* If a .ratst file is given (only possible via command line), * initialize test sequence. */ if (info) - input_test_file_read(info->path); + load_result = input_test_file_read(info->path); + if (last_test_step > MAX_TEST_STEPS) current_test_step = last_test_step; else @@ -1402,7 +1420,25 @@ bool NETRETROPAD_CORE_PREFIX(retro_load_game)(const struct retro_game_info *info NETRETROPAD_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_MESSAGE, &message); } - return true; + if (!load_result) + { + const char msg[] = "Invalid test input file!"; + struct retro_error_message e; + e.code = RETROE_UNSUPPORTED_CONTENT_FORMAT; + e.message = msg; + NETRETROPAD_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_ERROR_CODE, &e); + } + + if (false) + { + const char msg[] = "Simulated load error - BIOS!"; + struct retro_error_message e; + e.code = RETROE_MISSING_BIOS | 0x4321; + e.message = msg; + NETRETROPAD_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_ERROR_CODE, &e); + load_result = false; + } + return load_result; } void NETRETROPAD_CORE_PREFIX(retro_unload_game)(void) { } @@ -1411,9 +1447,23 @@ bool NETRETROPAD_CORE_PREFIX(retro_load_game_special)(unsigned type, const struct retro_game_info *info, size_t num) { return false; } size_t NETRETROPAD_CORE_PREFIX(retro_serialize_size)(void) { return 0; } bool NETRETROPAD_CORE_PREFIX(retro_serialize)(void *data, - size_t len) { return false; } + size_t len) +{ + struct retro_error_message e; + e.code = RETROE_UNSUPPORTED_ACTION_SERIALIZE; + e.message = NULL; + NETRETROPAD_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_ERROR_CODE, &e); + return false; +} bool NETRETROPAD_CORE_PREFIX(retro_unserialize)(const void *data, - size_t len) { return false; } + size_t len) +{ + struct retro_error_message e; + e.code = RETROE_UNSUPPORTED_ACTION_UNSERIALIZE; + e.message = NULL; + NETRETROPAD_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_ERROR_CODE, &e); + return false; +} size_t NETRETROPAD_CORE_PREFIX(retro_get_memory_size)( unsigned id) { return 0; } void NETRETROPAD_CORE_PREFIX(retro_cheat_reset)(void) { } diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 85f9341d8be6..f80eceb564fc 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -511,6 +511,69 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) } #endif +#define ERROR_CODE_CASE(CODE) \ + case CODE :\ + return strlcpy(s, msg_hash_to_str(MSG_##CODE), len);\ + break; + +int msg_hash_get_error_msg_us_enum(enum retro_error err, char *s, size_t len) +{ + settings_t *settings = config_get_ptr(); + + switch (err & RETROE_MASK_FRONTEND) + { + ERROR_CODE_CASE(RETROE_UNSUPPORTED_CONTENT) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_CONTENT_ISO_FORMAT_ERROR) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_CONTENT_CHD_FORMAT_ERROR) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_CONTENT_CUE_FORMAT_ERROR) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_CONTENT_BIN_FORMAT_ERROR) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_CONTENT_ZIP_FORMAT_ERROR) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_CONTENT_7Z_FORMAT_ERROR) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_CONTENT_FORMAT) + ERROR_CODE_CASE(RETROE_MISSING_BIOS) + ERROR_CODE_CASE(RETROE_MISSING_BIOS_REGION_PAL) + ERROR_CODE_CASE(RETROE_MISSING_BIOS_REGION_NTSC) + ERROR_CODE_CASE(RETROE_MISSING_BIOS_REGION_WORLD) + ERROR_CODE_CASE(RETROE_MISSING_BIOS_REGION_USA) + ERROR_CODE_CASE(RETROE_MISSING_BIOS_REGION_JAPAN) + ERROR_CODE_CASE(RETROE_MISSING_BIOS_REGION_EUROPE) + ERROR_CODE_CASE(RETROE_MISSING_BIOS_REGION_BRAZIL) + ERROR_CODE_CASE(RETROE_MISSING_BIOS_REGION_COUNTRY) + ERROR_CODE_CASE(RETROE_MISSING_SYSTEM_FILES) + ERROR_CODE_CASE(RETROE_HARDWARE_RENDERING) + ERROR_CODE_CASE(RETROE_HARDWARE_RENDERING_VULKAN_NOT_AVAILABLE) + ERROR_CODE_CASE(RETROE_HARDWARE_RENDERING_VULKAN_VERSION_ERROR) + ERROR_CODE_CASE(RETROE_HARDWARE_RENDERING_OPENGL_NOT_AVAILABLE) + ERROR_CODE_CASE(RETROE_HARDWARE_RENDERING_OPENGL_VERSION_ERROR) + ERROR_CODE_CASE(RETROE_HARDWARE_RENDERING_DX11_NOT_AVAILABLE) + ERROR_CODE_CASE(RETROE_HARDWARE_RENDERING_DX12_NOT_AVAILABLE) + ERROR_CODE_CASE(RETROE_HARDWARE_RENDERING_PXFMT_XRGB8888_UNSUPP) + ERROR_CODE_CASE(RETROE_HARDWARE_RENDERING_PXFMT_RGB565_UNSUPP) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_ACTION) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_ACTION_SERIALIZE) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_ACTION_UNSERIALIZE) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_ACTION_UNSERIALIZE_FORMAT) + ERROR_CODE_CASE(RETROE_UNSUPPORTED_ACTION_CORE_OPTION_COMBI) + + default: + if ( err > RETROE_UNSUPPORTED_CONTENT && + err < RETROE_UNSUPPORTED_CONTENT_RANGE_END) + return strlcpy(s, msg_hash_to_str(MSG_RETROE_UNSUPPORTED_CONTENT), len); + else if ( err > RETROE_MISSING_BIOS && + err < RETROE_MISSING_BIOS_RANGE_END) + return strlcpy(s, msg_hash_to_str(MSG_RETROE_MISSING_BIOS), len); + else if ( err > RETROE_HARDWARE_RENDERING && + err < RETROE_HARDWARE_RENDERING_RANGE_END) + return strlcpy(s, msg_hash_to_str(MSG_RETROE_HARDWARE_RENDERING), len); + else if ( err > RETROE_UNSUPPORTED_ACTION && + err < RETROE_UNSUPPORTED_ACTION_RANGE_END) + return strlcpy(s, msg_hash_to_str(MSG_RETROE_UNSUPPORTED_ACTION), len); + else + return strlcpy(s, msg_hash_to_str(MSG_RETROE_UNKNOWN), len); + } + return 0; +} + #ifdef HAVE_MENU static const char *menu_hash_to_str_us_label_enum(enum msg_hash_enums msg) { diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 97244ed5235d..196457fc2ddf 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -16707,6 +16707,138 @@ MSG_HASH( MSG_AI_SERVICE_STOPPED, "stopped." ) +MSG_HASH( + MSG_RETROE_UNKNOWN, + "Unknown error code received from core." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_CONTENT, + "This core does not support this kind of content." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_CONTENT_ISO_FORMAT_ERROR, + "This core does not support the specific ISO format of this content." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_CONTENT_CHD_FORMAT_ERROR, + "This core does not support the specific CHD format of this content." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_CONTENT_CUE_FORMAT_ERROR, + "This core does not support the specific CUE format of this content." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_CONTENT_BIN_FORMAT_ERROR, + "This core does not support the specific BIN format of this content." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_CONTENT_ZIP_FORMAT_ERROR, + "This core does not support the specific ZIP format of this content." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_CONTENT_7Z_FORMAT_ERROR, + "This core does not support the specific 7Z format of this content." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_CONTENT_FORMAT, + "This core does not support the specific format of this content." + ) +MSG_HASH( + MSG_RETROE_MISSING_BIOS, + "Necessary BIOS file(s) to run this content are not present." + ) +MSG_HASH( + MSG_RETROE_MISSING_BIOS_REGION_PAL, + "Necessary PAL region BIOS file(s) to run this content are not present." + ) +MSG_HASH( + MSG_RETROE_MISSING_BIOS_REGION_NTSC, + "Necessary NTSC region BIOS file(s) to run this content are not present." + ) +MSG_HASH( + MSG_RETROE_MISSING_BIOS_REGION_WORLD, + "Necessary world region BIOS file(s) to run this content are not present." + ) +MSG_HASH( + MSG_RETROE_MISSING_BIOS_REGION_USA, + "Necessary USA region BIOS file(s) to run this content are not present." + ) +MSG_HASH( + MSG_RETROE_MISSING_BIOS_REGION_JAPAN, + "Necessary Japan region BIOS file(s) to run this content are not present." + ) +MSG_HASH( + MSG_RETROE_MISSING_BIOS_REGION_EUROPE, + "Necessary Europe region BIOS file(s) to run this content are not present." + ) +MSG_HASH( + MSG_RETROE_MISSING_BIOS_REGION_BRAZIL, + "Necessary Brazil region BIOS file(s) to run this content are not present." + ) +MSG_HASH( + MSG_RETROE_MISSING_BIOS_REGION_COUNTRY, + "Necessary country specific BIOS file(s) to run this content are not present." + ) +MSG_HASH( + MSG_RETROE_MISSING_SYSTEM_FILES, + "Necessary system file(s) to run this content are not present, they may be retrieved with the online updater." + ) +MSG_HASH( + MSG_RETROE_HARDWARE_RENDERING, + "This core would require a hardware rendering feature that is not available." + ) +MSG_HASH( + MSG_RETROE_HARDWARE_RENDERING_VULKAN_NOT_AVAILABLE, + "This core would require Vulkan rendering that is not available." + ) +MSG_HASH( + MSG_RETROE_HARDWARE_RENDERING_VULKAN_VERSION_ERROR, + "This core would require a Vulkan rendering version that is not available." + ) +MSG_HASH( + MSG_RETROE_HARDWARE_RENDERING_OPENGL_NOT_AVAILABLE, + "This core would require OpenGL rendering that is not available." + ) +MSG_HASH( + MSG_RETROE_HARDWARE_RENDERING_OPENGL_VERSION_ERROR, + "This core would require an OpenGL rendering version that is not available." + ) +MSG_HASH( + MSG_RETROE_HARDWARE_RENDERING_DX11_NOT_AVAILABLE, + "This core would require DirectX 11 rendering that is not available." + ) +MSG_HASH( + MSG_RETROE_HARDWARE_RENDERING_DX12_NOT_AVAILABLE, + "This core would require DirectX 12 rendering that is not available." + ) +MSG_HASH( + MSG_RETROE_HARDWARE_RENDERING_PXFMT_XRGB8888_UNSUPP, + "This core would require pixel format XRGB8888 that is not available." + ) +MSG_HASH( + MSG_RETROE_HARDWARE_RENDERING_PXFMT_RGB565_UNSUPP, + "This core would require pixel format RGB565 that is not available." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_ACTION, + "This core does not support this action." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_ACTION_SERIALIZE, + "This core does not support serialization (save states)." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_ACTION_UNSERIALIZE, + "This core does not support unserialization (save state loading)." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_ACTION_UNSERIALIZE_FORMAT, + "Error encountered with save state format." + ) +MSG_HASH( + MSG_RETROE_UNSUPPORTED_ACTION_CORE_OPTION_COMBI, + "This combination of core options is invalid." + ) #ifdef HAVE_GAME_AI MSG_HASH( MENU_ENUM_LABEL_VALUE_GAME_AI_MENU_OPTION, diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 099b681b922c..478db35fa3eb 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -711,6 +711,78 @@ enum retro_mod RETROKMOD_DUMMY = INT_MAX /* Ensure sizeof(enum) == sizeof(int) */ }; +/** + * Error code ranges and specific errors that may be reported by cores. + * Explicitly listed errors within a range may have localized error + * messages maintained by the frontend. The first item in the range + * acts as a generic error indication for that kind of error. + * + * Frontend codes not in the list may be sent, but not encouraged as + * later updates may extend the mapping. + * Lower 16 bits are reserved for the core and can be freely defined. + * @see RETRO_ENVIRONMENT_SET_ERROR_CODE + */ +enum retro_error +{ + RETROE_NONE = 0x00000000, + + /* A problem with the content not covered by more specific codes */ + RETROE_UNSUPPORTED_CONTENT = 0x01000000, + /* The format is recognized, but internal contents are not appropriate for the core */ + RETROE_UNSUPPORTED_CONTENT_ISO_FORMAT_ERROR = 0x01010000, + RETROE_UNSUPPORTED_CONTENT_CHD_FORMAT_ERROR = 0x01020000, + RETROE_UNSUPPORTED_CONTENT_CUE_FORMAT_ERROR = 0x01030000, + RETROE_UNSUPPORTED_CONTENT_BIN_FORMAT_ERROR = 0x01040000, + RETROE_UNSUPPORTED_CONTENT_ZIP_FORMAT_ERROR = 0x01050000, + RETROE_UNSUPPORTED_CONTENT_7Z_FORMAT_ERROR = 0x01060000, + /* The format is not recognized. */ + RETROE_UNSUPPORTED_CONTENT_FORMAT = 0x01100000, + RETROE_UNSUPPORTED_CONTENT_RANGE_END = 0x01FFFFFF, + /* A problem with firmware / BIOS not covered by more specific codes. */ + RETROE_MISSING_BIOS = 0x02000000, + /* Content would need region specific BIOS that is not found */ + RETROE_MISSING_BIOS_REGION_PAL = 0x02010000, + RETROE_MISSING_BIOS_REGION_NTSC = 0x02020000, + RETROE_MISSING_BIOS_REGION_WORLD = 0x02030000, + /* Content would need country specific BIOS that is not found */ + RETROE_MISSING_BIOS_REGION_USA = 0x02100000, + RETROE_MISSING_BIOS_REGION_JAPAN = 0x02110000, + RETROE_MISSING_BIOS_REGION_EUROPE = 0x02120000, + RETROE_MISSING_BIOS_REGION_BRAZIL = 0x02130000, + /* Content would need other country specific BIOS that is not found */ + RETROE_MISSING_BIOS_REGION_COUNTRY = 0x02200000, + /* Content would need system files from online updater */ + RETROE_MISSING_SYSTEM_FILES = 0x02300000, + RETROE_MISSING_BIOS_RANGE_END = 0x02FFFFFF, + /* A problem with the available display rendering from the frontend, + * not covered by more specific codes. */ + RETROE_HARDWARE_RENDERING = 0x03000000, + /* Specific API not available in general or with the required version */ + RETROE_HARDWARE_RENDERING_VULKAN_NOT_AVAILABLE = 0x03010000, + RETROE_HARDWARE_RENDERING_VULKAN_VERSION_ERROR = 0x03020000, + RETROE_HARDWARE_RENDERING_OPENGL_NOT_AVAILABLE = 0x03030000, + RETROE_HARDWARE_RENDERING_OPENGL_VERSION_ERROR = 0x03040000, + RETROE_HARDWARE_RENDERING_DX11_NOT_AVAILABLE = 0x03050000, + RETROE_HARDWARE_RENDERING_DX12_NOT_AVAILABLE = 0x03060000, + /* Specific pixel format not available */ + RETROE_HARDWARE_RENDERING_PXFMT_XRGB8888_UNSUPP = 0x03110000, + RETROE_HARDWARE_RENDERING_PXFMT_RGB565_UNSUPP = 0x03120000, + RETROE_HARDWARE_RENDERING_RANGE_END = 0x03FFFFFF, + /* The action from the frontend is not supported by the core */ + RETROE_UNSUPPORTED_ACTION = 0x04000000, + /* (Un)serialization (save state, netplay) not available */ + RETROE_UNSUPPORTED_ACTION_SERIALIZE = 0x04010000, + RETROE_UNSUPPORTED_ACTION_UNSERIALIZE = 0x04020000, + /* Unserialization is available but the actual file is not compatible */ + RETROE_UNSUPPORTED_ACTION_UNSERIALIZE_FORMAT = 0x04030000, + /* The currently selected core option combination is not valid */ + RETROE_UNSUPPORTED_ACTION_CORE_OPTION_COMBI = 0x04040000, + RETROE_UNSUPPORTED_ACTION_RANGE_END = 0x04FFFFFF, + + RETROE_DUMMY = INT_MAX /* Ensure sizeof(enum) == sizeof(int) */ +}; +#define RETROE_MASK_CORE 0x0000FFFF +#define RETROE_MASK_FRONTEND 0xFFFF0000 /** * @defgroup RETRO_ENVIRONMENT Environment Callbacks * @{ @@ -2574,6 +2646,16 @@ enum retro_mod */ #define RETRO_ENVIRONMENT_GET_FILE_BROWSER_START_DIRECTORY 80 +/** + * Informs the frontend of an error occurrence during the current operation. + * Contains a code and an optional (short) message to be displayed. + * + * @param[out] data retro_error_message *. + * @return \c true if the environment call is available. + * @see retro_error + */ +#define RETRO_ENVIRONMENT_SET_ERROR_CODE 81 + /**@}*/ /** @@ -7380,6 +7462,20 @@ struct retro_device_power int8_t percent; }; +struct retro_error_message +{ + /** + * An error code and an optional message for sending feedback to the frontend. + */ + enum retro_error code; + + /** + * A short message for the user to be displayed by the frontend. Optional. + */ + const char* message; + +}; + /** @} */ /** diff --git a/msg_hash.c b/msg_hash.c index 79726bd7f204..000d6d3fca74 100644 --- a/msg_hash.c +++ b/msg_hash.c @@ -54,6 +54,11 @@ int msg_hash_get_help_enum(enum msg_hash_enums msg, char *s, size_t len) return ret; } +int msg_hash_get_error_msg_enum(enum retro_error err, char *s, size_t len) +{ + return msg_hash_get_error_msg_us_enum(err, s, len); +} + const char *get_user_language_iso639_1(bool limit) { switch (uint_user_language) diff --git a/msg_hash.h b/msg_hash.h index 642b9886f6c8..fa9f384d64a9 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "input/input_defines.h" @@ -4294,6 +4295,39 @@ enum msg_hash_enums MSG_3DS_BOTTOM_MENU_SAVE_STATE, MSG_3DS_BOTTOM_MENU_LOAD_STATE, + MSG_RETROE_UNKNOWN, + MSG_RETROE_UNSUPPORTED_CONTENT, + MSG_RETROE_UNSUPPORTED_CONTENT_ISO_FORMAT_ERROR, + MSG_RETROE_UNSUPPORTED_CONTENT_CHD_FORMAT_ERROR, + MSG_RETROE_UNSUPPORTED_CONTENT_CUE_FORMAT_ERROR, + MSG_RETROE_UNSUPPORTED_CONTENT_BIN_FORMAT_ERROR, + MSG_RETROE_UNSUPPORTED_CONTENT_ZIP_FORMAT_ERROR, + MSG_RETROE_UNSUPPORTED_CONTENT_7Z_FORMAT_ERROR, + MSG_RETROE_UNSUPPORTED_CONTENT_FORMAT, + MSG_RETROE_MISSING_BIOS, + MSG_RETROE_MISSING_BIOS_REGION_PAL, + MSG_RETROE_MISSING_BIOS_REGION_NTSC, + MSG_RETROE_MISSING_BIOS_REGION_WORLD, + MSG_RETROE_MISSING_BIOS_REGION_USA, + MSG_RETROE_MISSING_BIOS_REGION_JAPAN, + MSG_RETROE_MISSING_BIOS_REGION_EUROPE, + MSG_RETROE_MISSING_BIOS_REGION_BRAZIL, + MSG_RETROE_MISSING_BIOS_REGION_COUNTRY, + MSG_RETROE_MISSING_SYSTEM_FILES, + MSG_RETROE_HARDWARE_RENDERING, + MSG_RETROE_HARDWARE_RENDERING_VULKAN_NOT_AVAILABLE, + MSG_RETROE_HARDWARE_RENDERING_VULKAN_VERSION_ERROR, + MSG_RETROE_HARDWARE_RENDERING_OPENGL_NOT_AVAILABLE, + MSG_RETROE_HARDWARE_RENDERING_OPENGL_VERSION_ERROR, + MSG_RETROE_HARDWARE_RENDERING_DX11_NOT_AVAILABLE, + MSG_RETROE_HARDWARE_RENDERING_DX12_NOT_AVAILABLE, + MSG_RETROE_HARDWARE_RENDERING_PXFMT_XRGB8888_UNSUPP, + MSG_RETROE_HARDWARE_RENDERING_PXFMT_RGB565_UNSUPP, + MSG_RETROE_UNSUPPORTED_ACTION, + MSG_RETROE_UNSUPPORTED_ACTION_SERIALIZE, + MSG_RETROE_UNSUPPORTED_ACTION_UNSERIALIZE, + MSG_RETROE_UNSUPPORTED_ACTION_UNSERIALIZE_FORMAT, + MSG_RETROE_UNSUPPORTED_ACTION_CORE_OPTION_COMBI, #ifdef HAVE_GAME_AI MENU_LABEL(QUICK_MENU_SHOW_GAME_AI), @@ -4313,11 +4347,13 @@ enum msg_hash_enums /* Callback strings */ const char *msg_hash_to_str(enum msg_hash_enums msg); - const char *msg_hash_to_str_us(enum msg_hash_enums msg); -int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len); int msg_hash_get_help_enum(enum msg_hash_enums msg, char *s, size_t len); +int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len); + +int msg_hash_get_error_msg_enum(enum retro_error err, char *s, size_t len); +int msg_hash_get_error_msg_us_enum(enum retro_error err, char *s, size_t len); enum msg_file_type msg_hash_to_file_type(uint32_t hash); diff --git a/runloop.c b/runloop.c index 3d0ea1ce2125..101d74ef7bdd 100644 --- a/runloop.c +++ b/runloop.c @@ -320,6 +320,10 @@ x(retro_get_memory_data); \ x(retro_get_memory_size); +#define RESET_ERROR_CODE() \ + runloop_st->last_error_code = 0;\ + runloop_st->last_error_message[0] = '\0'; + #ifdef _WIN32 #define PERF_LOG_FMT "[PERF]: Avg (%s): %I64u ticks, %I64u runs.\n" #else @@ -407,6 +411,60 @@ static void runloop_perf_log(void) runloop_state.perf_ptr_libretro); } +#define LOG_ERROR_CODE(ERR, FUNC) \ + RARCH_##ERR("[Error code]: Code received in %s: %04X-%04X \"%s\".\n",\ + FUNC,\ + (runloop_st->last_error_code & RETROE_MASK_FRONTEND ) >> 16,\ + runloop_st->last_error_code & RETROE_MASK_CORE,\ + runloop_st->last_error_message ); + +static void runloop_display_error_code(const char *err, const char *func) +{ + runloop_state_t *runloop_st = &runloop_state; + enum message_queue_category cat; + + if (strcmp(err, "ERR" ) == 0) + { + cat = MESSAGE_QUEUE_CATEGORY_ERROR; + LOG_ERROR_CODE(ERR,func); + } + else if (strcmp(err, "WARN" ) == 0) + { + cat = MESSAGE_QUEUE_CATEGORY_WARNING; + LOG_ERROR_CODE(WARN,func); + } + else if (strcmp(err, "LOG" ) == 0) + { + cat = MESSAGE_QUEUE_CATEGORY_INFO; + LOG_ERROR_CODE(LOG,func); + } + else + { + cat = MESSAGE_QUEUE_CATEGORY_SUCCESS; + LOG_ERROR_CODE(DBG,func); + } + + if (cat != MESSAGE_QUEUE_CATEGORY_SUCCESS) + { + char msg[512]; + size_t _len = 0; + + _len = snprintf(msg + _len, sizeof(msg) - _len, "%04X-%04x: ", + (runloop_st->last_error_code & RETROE_MASK_FRONTEND) >> 16, + runloop_st->last_error_code & RETROE_MASK_CORE); + + if (runloop_st->last_error_message[0] != '\0') + _len += strlcpy(msg + _len, runloop_st->last_error_message, + sizeof(msg) - _len); + else + _len += msg_hash_get_error_msg_enum(runloop_st->last_error_code, + msg + _len, sizeof(msg) - _len); + + runloop_msg_queue_push(msg, _len, + 2, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, cat); + } + +} static bool runloop_environ_cb_get_system_info(unsigned cmd, void *data) { runloop_state_t *runloop_st = &runloop_state; @@ -3554,6 +3612,23 @@ bool runloop_environment_cb(unsigned cmd, void *data) } } break; + + case RETRO_ENVIRONMENT_SET_ERROR_CODE: + { + + RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_ERROR_CODE.\n"); + RESET_ERROR_CODE(); + if (data) + { + struct retro_error_message *msg = (struct retro_error_message *)data; + runloop_st->last_error_code = msg->code; + if (msg->message) + strlcpy(runloop_st->last_error_message,msg->message,sizeof(runloop_st->last_error_message)); + runloop_display_error_code("DBG", "retro_environment_set"); + } + } + break; + default: RARCH_LOG("[Environ]: UNSUPPORTED (#%u).\n", cmd); return false; @@ -3941,7 +4016,12 @@ static bool core_unload_game(void) if ((runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED)) { RARCH_LOG("[Core]: Unloading game..\n"); + + RESET_ERROR_CODE(); runloop_st->current_core.retro_unload_game(); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_unload_game"); + runloop_st->core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE; runloop_st->current_core.flags &= ~RETRO_CORE_FLAG_GAME_LOADED; } @@ -4046,7 +4126,10 @@ void runloop_event_deinit_core(void) if (runloop_st->current_core.flags & RETRO_CORE_FLAG_INITED) { RARCH_LOG("[Core]: Unloading core..\n"); + RESET_ERROR_CODE(); runloop_st->current_core.retro_deinit(); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "deinit_core"); } /* retro_deinit() may call @@ -4487,7 +4570,13 @@ static void retro_run_null(void) { } /* Stub function callback impl. */ static bool core_verify_api_version(runloop_state_t *runloop_st) { - unsigned api_version = runloop_st->current_core.retro_api_version(); + unsigned api_version; + + RESET_ERROR_CODE(); + api_version = runloop_st->current_core.retro_api_version(); + if (runloop_st->last_error_code) + runloop_display_error_code("ERR", "core_verify_api_version"); + if (api_version != RETRO_API_VERSION) { RARCH_WARN("[Core]: %s\n", msg_hash_to_str(MSG_LIBRETRO_ABI_BREAK)); @@ -4552,11 +4641,14 @@ static void core_init_libretro_cbs(runloop_state_t *runloop_st, { retro_input_state_t state_cb = core_input_state_poll_return_cb(); + RESET_ERROR_CODE(); runloop_st->current_core.retro_set_video_refresh(video_driver_frame); runloop_st->current_core.retro_set_audio_sample(audio_driver_sample); runloop_st->current_core.retro_set_audio_sample_batch(audio_driver_sample_batch); runloop_st->current_core.retro_set_input_state(state_cb); runloop_st->current_core.retro_set_input_poll(core_input_state_poll_maybe); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_init_libretro_cbs"); runloop_st->input_poll_callback_original = core_input_state_poll_maybe; @@ -4579,7 +4671,10 @@ static bool runloop_event_load_core(runloop_state_t *runloop_st, return false; core_init_libretro_cbs(runloop_st, &runloop_st->retro_ctx); + RESET_ERROR_CODE(); runloop_st->current_core.retro_get_system_av_info(&video_st->av_info); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "load_code get_system_av_info"); RARCH_LOG("[Core]: Geometry: %ux%u, Aspect: %.3f, FPS: %.2f, Sample rate: %.2f Hz.\n", video_st->av_info.geometry.base_width, video_st->av_info.geometry.base_height, @@ -4635,7 +4730,11 @@ bool runloop_event_init_core( if (!runloop_st->current_core.retro_run) runloop_st->current_core.retro_run = retro_run_null; runloop_st->current_core.flags |= RETRO_CORE_FLAG_SYMBOLS_INITED; + + RESET_ERROR_CODE(); runloop_st->current_core.retro_get_system_info(&sys_info->info); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "init_core get_system_info"); if (!sys_info->info.library_name) sys_info->info.library_name = msg_hash_to_str(MSG_UNKNOWN); @@ -4705,7 +4804,10 @@ bool runloop_event_init_core( runloop_path_set_redirect(settings, old_savefile_dir, old_savestate_dir); /* Set core environment */ + RESET_ERROR_CODE(); runloop_st->current_core.retro_set_environment(runloop_environment_cb); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "init_core core_set_environment"); /* Load any input remap files * > Note that we always cache the current global @@ -4723,8 +4825,11 @@ bool runloop_event_init_core( video_st->frame_cache_data = NULL; + RESET_ERROR_CODE(); runloop_st->current_core.retro_init(); runloop_st->current_core.flags |= RETRO_CORE_FLAG_INITED; + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "init_core init"); /* Attempt to set initial disk index */ if (initial_disk_change_enable) @@ -7403,6 +7508,7 @@ bool core_set_netplay_callbacks(void) if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_USE_CORE_PACKET_INTERFACE, NULL)) { + RESET_ERROR_CODE(); /* Force normal poll type for netplay. */ runloop_st->current_core.poll_type = POLL_TYPE_NORMAL; @@ -7411,6 +7517,8 @@ bool core_set_netplay_callbacks(void) runloop_st->current_core.retro_set_audio_sample(audio_sample_net); runloop_st->current_core.retro_set_audio_sample_batch(audio_sample_batch_net); runloop_st->current_core.retro_set_input_state(input_state_net); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_set_netplay_callbacks"); } return true; @@ -7430,10 +7538,13 @@ bool core_unset_netplay_callbacks(void) if (!core_set_default_callbacks(&cbs)) return false; + RESET_ERROR_CODE(); runloop_st->current_core.retro_set_video_refresh(cbs.frame_cb); runloop_st->current_core.retro_set_audio_sample(cbs.sample_cb); runloop_st->current_core.retro_set_audio_sample_batch(cbs.sample_batch_cb); runloop_st->current_core.retro_set_input_state(cbs.state_cb); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_unset_netplay_callbacks"); return true; } @@ -7464,16 +7575,26 @@ bool core_set_cheat(retro_ctx_cheat_info_t *info) } #endif + RESET_ERROR_CODE(); runloop_st->current_core.retro_cheat_set(info->index, info->enabled, info->code); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_cheat_set"); + #if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)) if ( (want_runahead) && (run_ahead_secondary_instance) && (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE) && (secondary_core_ensure_exists(runloop_st, settings)) && (runloop_st->secondary_core.retro_cheat_set)) + { + RESET_ERROR_CODE(); runloop_st->secondary_core.retro_cheat_set( info->index, info->enabled, info->code); + + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_cheat_set, second instance"); + } #endif return true; @@ -7504,7 +7625,10 @@ bool core_reset_cheat(void) } #endif + RESET_ERROR_CODE(); runloop_st->current_core.retro_cheat_reset(); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_cheat_reset"); #if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)) if ( (want_runahead) @@ -7512,7 +7636,12 @@ bool core_reset_cheat(void) && (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE) && (secondary_core_ensure_exists(runloop_st, settings)) && (runloop_st->secondary_core.retro_cheat_reset)) + { + RESET_ERROR_CODE(); runloop_st->secondary_core.retro_cheat_reset(); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_cheat_reset, second instance"); + } #endif return true; @@ -7549,7 +7678,11 @@ bool core_set_controller_port_device(retro_ctx_controller_info_t *pad) #endif #endif + RESET_ERROR_CODE(); runloop_st->current_core.retro_set_controller_port_device(pad->port, pad->device); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_set_controller_port_device"); + return true; } @@ -7558,8 +7691,12 @@ bool core_get_memory(retro_ctx_memory_info_t *info) runloop_state_t *runloop_st = &runloop_state; if (!info) return false; + RESET_ERROR_CODE(); info->size = runloop_st->current_core.retro_get_memory_size(info->id); info->data = runloop_st->current_core.retro_get_memory_data(info->id); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_get_memory"); + return true; } @@ -7579,6 +7716,7 @@ bool core_load_game(retro_ctx_load_content_info_t *load_info) #endif set_save_state_in_background(false); + RESET_ERROR_CODE(); if (load_info && load_info->special) game_loaded = runloop_st->current_core.retro_load_game_special( @@ -7590,6 +7728,9 @@ bool core_load_game(retro_ctx_load_content_info_t *load_info) if (game_loaded) { + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_load_game"); + /* If 'game_loaded' is true at this point, then * core is actually running; register that any * changes to global remap-related parameters @@ -7604,6 +7745,9 @@ bool core_load_game(retro_ctx_load_content_info_t *load_info) return true; } + if (runloop_st->last_error_code) + runloop_display_error_code("ERR", "core_load_game"); + runloop_st->current_core.flags &= ~RETRO_CORE_FLAG_GAME_LOADED; return false; } @@ -7611,17 +7755,33 @@ bool core_load_game(retro_ctx_load_content_info_t *load_info) bool core_get_system_info(struct retro_system_info *sysinfo) { runloop_state_t *runloop_st = &runloop_state; + if (!sysinfo) return false; + + RESET_ERROR_CODE(); runloop_st->current_core.retro_get_system_info(sysinfo); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_get_system_info"); + return true; } bool core_unserialize(retro_ctx_serialize_info_t *info) { runloop_state_t *runloop_st = &runloop_state; + + RESET_ERROR_CODE(); if (!info || !runloop_st->current_core.retro_unserialize(info->data_const, info->size)) + { + if (runloop_st->last_error_code) + runloop_display_error_code("ERR", "core_unserialize"); + return false; + } + + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_unserialize"); #ifdef HAVE_NETWORKING netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info); @@ -7641,10 +7801,15 @@ bool core_unserialize_special(retro_ctx_serialize_info_t *info) if (!info) return false; + RESET_ERROR_CODE(); + runloop_st->flags |= RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE; ret = runloop_st->current_core.retro_unserialize(info->data_const, info->size); runloop_st->flags &= ~RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE; + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_unserialize_special"); + #ifdef HAVE_NETWORKING if (ret) netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info); @@ -7656,8 +7821,19 @@ bool core_unserialize_special(retro_ctx_serialize_info_t *info) bool core_serialize(retro_ctx_serialize_info_t *info) { runloop_state_t *runloop_st = &runloop_state; + + RESET_ERROR_CODE(); if (!info || !runloop_st->current_core.retro_serialize(info->data, info->size)) + { + if (runloop_st->last_error_code) + runloop_display_error_code("ERR", "core_serialize"); + return false; + } + + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_serialize"); + return true; } @@ -7669,28 +7845,42 @@ bool core_serialize_special(retro_ctx_serialize_info_t *info) if (!info) return false; + RESET_ERROR_CODE(); runloop_st->flags |= RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE; ret = runloop_st->current_core.retro_serialize( info->data, info->size); runloop_st->flags &= ~RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE; + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_serialize_special"); + return ret; } size_t core_serialize_size(void) { + size_t val; runloop_state_t *runloop_st = &runloop_state; - return runloop_st->current_core.retro_serialize_size(); + RESET_ERROR_CODE(); + val = runloop_st->current_core.retro_serialize_size(); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_serialize_size"); + + return val; } size_t core_serialize_size_special(void) { size_t val; runloop_state_t *runloop_st = &runloop_state; + RESET_ERROR_CODE(); runloop_st->flags |= RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE; val = runloop_st->current_core.retro_serialize_size(); runloop_st->flags &= ~RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE; + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_serialize_size_special"); + return val; } @@ -7705,7 +7895,13 @@ void core_reset(void) runloop_state_t *runloop_st = &runloop_state; video_driver_state_t *video_st = video_state_get_ptr(); video_st->frame_cache_data = NULL; + + RESET_ERROR_CODE(); runloop_st->current_core.retro_reset(); + + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_reset"); + } void core_run(void) @@ -7738,9 +7934,13 @@ void core_run(void) input_driver_poll(); else if (late_polling) current_core->flags &= ~RETRO_CORE_FLAG_INPUT_POLLED; + RESET_ERROR_CODE(); current_core->retro_run(); + if (runloop_st->last_error_code) + runloop_display_error_code("LOG", "core_run"); + #ifdef HAVE_GAME_AI { settings_t *settings = config_get_ptr(); diff --git a/runloop.h b/runloop.h index 40f104e28b57..fb31783570f1 100644 --- a/runloop.h +++ b/runloop.h @@ -267,6 +267,8 @@ struct runloop #endif uint32_t flags; + uint32_t last_error_code; + char last_error_message[512]; int8_t run_frames_and_pause; char runtime_content_path_basename[PATH_MAX_LENGTH];