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];