diff --git a/src/drivers/libretro/libretro-common/file/file_path.c b/src/drivers/libretro/libretro-common/file/file_path.c index 8f49c0d..c696ff0 100644 --- a/src/drivers/libretro/libretro-common/file/file_path.c +++ b/src/drivers/libretro/libretro-common/file/file_path.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -68,6 +69,31 @@ #endif +/* Time format strings with AM-PM designation require special + * handling due to platform dependence */ +void strftime_am_pm(char *s, size_t len, const char* format, + const void *ptr) +{ + char *local = NULL; + const struct tm *timeptr = (const struct tm*)ptr; + + /* Ensure correct locale is set + * > Required for localised AM/PM strings */ + setlocale(LC_TIME, ""); + + strftime(s, len, format, timeptr); +#if !(defined(__linux__) && !defined(ANDROID)) + if ((local = local_to_utf8_string_alloc(s))) + { + if (!string_is_empty(local)) + strlcpy(s, local, len); + + free(local); + local = NULL; + } +#endif +} + /** * Create a new linked list with one node in it * The path on this node will be set to NULL @@ -172,9 +198,9 @@ const char *path_get_archive_delim(const char *path) string_to_lower(buf); /* Check if this is a '.zip', '.apk' or '.7z' file */ - if (string_is_equal(buf, ".zip") || - string_is_equal(buf, ".apk") || - string_is_equal(buf + 1, ".7z")) + if ( string_is_equal(buf, ".zip") + || string_is_equal(buf, ".apk") + || string_is_equal(buf + 1, ".7z")) return delim; } else if (delim - path > 3) @@ -208,7 +234,7 @@ const char *path_get_archive_delim(const char *path) const char *path_get_extension(const char *path) { const char *ext; - if (!string_is_empty(path) && ((ext = strrchr(path_basename(path), '.')))) + if (!string_is_empty(path) && ((ext = (char*)strrchr(path_basename(path), '.')))) return ext + 1; return ""; } @@ -228,7 +254,7 @@ const char *path_get_extension(const char *path) char *path_get_extension_mutable(const char *path) { char *ext = NULL; - if (!string_is_empty(path) && ((ext = strrchr(path_basename(path), '.')))) + if (!string_is_empty(path) && ((ext = (char*)strrchr(path_basename(path), '.')))) return ext; return NULL; } @@ -302,14 +328,16 @@ bool path_is_compressed_file(const char* path) size_t fill_pathname(char *out_path, const char *in_path, const char *replace, size_t size) { + size_t _len; char tmp_path[PATH_MAX_LENGTH]; char *tok = NULL; strlcpy(tmp_path, in_path, sizeof(tmp_path)); if ((tok = (char*)strrchr(path_basename(tmp_path), '.'))) *tok = '\0'; - strlcpy(out_path, tmp_path, size); - return strlcat(out_path, replace, size); + _len = strlcpy(out_path, tmp_path, size); + _len += strlcpy(out_path + _len, replace, size - _len); + return _len; } @@ -319,21 +347,20 @@ size_t fill_pathname(char *out_path, const char *in_path, * @size : size of path * * Find last slash in path. Tries to find - * a backslash on Windows too which takes precedence - * over regular slash. + * a backslash as used for Windows paths, + * otherwise checks for a regular slash. * @return pointer to last slash/backslash found in @str. **/ char *find_last_slash(const char *str) { const char *slash = strrchr(str, '/'); -#ifdef _WIN32 const char *backslash = strrchr(str, '\\'); if (!slash || (backslash > slash)) return (char*)backslash; -#endif - return (char*)slash; + else + return (char*)slash; } /** @@ -344,24 +371,20 @@ char *find_last_slash(const char *str) * Assumes path is a directory. Appends a slash * if not already there. **/ -void fill_pathname_slash(char *path, size_t size) +size_t fill_pathname_slash(char *path, size_t size) { size_t path_len; const char *last_slash = find_last_slash(path); - if (!last_slash) - { - strlcat(path, PATH_DEFAULT_SLASH(), size); - return; - } - - path_len = strlen(path); + return strlcat(path, PATH_DEFAULT_SLASH(), size); + path_len = strlen(path); /* Try to preserve slash type. */ if (last_slash != (path + path_len - 1)) { - path[path_len] = last_slash[0]; - path[path_len+1] = '\0'; + path[ path_len] = last_slash[0]; + path[++path_len] = '\0'; } + return path_len; } /** @@ -384,12 +407,11 @@ void fill_pathname_slash(char *path, size_t size) size_t fill_pathname_dir(char *in_dir, const char *in_basename, const char *replace, size_t size) { - const char *base = NULL; - - fill_pathname_slash(in_dir, size); - base = path_basename(in_basename); - strlcat(in_dir, base, size); - return strlcat(in_dir, replace, size); + size_t _len = fill_pathname_slash(in_dir, size); + const char *base = path_basename(in_basename); + _len += strlcpy(in_dir + _len, base, size - _len); + _len += strlcpy(in_dir + _len, replace, size - _len); + return _len; } /** @@ -514,14 +536,14 @@ void fill_pathname_parent_dir(char *out_dir, size_t fill_dated_filename(char *out_filename, const char *ext, size_t size) { - time_t cur_time = time(NULL); + size_t _len; struct tm tm_; - + time_t cur_time = time(NULL); rtime_localtime(&cur_time, &tm_); - - strftime(out_filename, size, + _len = strftime(out_filename, size, "RetroArch-%m%d-%H%M%S", &tm_); - return strlcat(out_filename, ext, size); + _len += strlcpy(out_filename + _len, ext, size - _len); + return _len; } /** @@ -542,21 +564,24 @@ size_t fill_dated_filename(char *out_filename, size_t fill_str_dated_filename(char *out_filename, const char *in_str, const char *ext, size_t size) { - char format[NAME_MAX_LENGTH]; struct tm tm_; + char format[NAME_MAX_LENGTH]; + size_t _len = 0; time_t cur_time = time(NULL); - rtime_localtime(&cur_time, &tm_); - - strlcpy(out_filename, in_str, size); + _len = strlcpy(out_filename, in_str, size); if (string_is_empty(ext)) { strftime(format, sizeof(format), "-%y%m%d-%H%M%S", &tm_); - return strlcat(out_filename, format, size); + _len += strlcpy(out_filename + _len, format, size - _len); } - strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", &tm_); - strlcat(out_filename, format, size); - return strlcat(out_filename, ext, size); + else + { + strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", &tm_); + _len += strlcpy(out_filename + _len, format, size - _len); + _len += strlcpy(out_filename + _len, ext, size - _len); + } + return _len; } /** @@ -626,18 +651,12 @@ void path_parent_dir(char *path, size_t len) **/ const char *path_basename(const char *path) { - /* We cut at the first compression-related hash */ - const char *delim = path_get_archive_delim(path); - if (delim) - return delim + 1; - - { - /* We cut at the last slash */ - const char *last = find_last_slash(path); - if (last) - return last + 1; - } - + /* We cut either at the first compression-related hash, + * or we cut at the last slash */ + const char *ptr = NULL; + if ( (ptr = path_get_archive_delim(path)) + || (ptr = find_last_slash(path))) + return ptr + 1; return path; } @@ -670,24 +689,23 @@ const char *path_basename_nocompression(const char *path) **/ bool path_is_absolute(const char *path) { - if (string_is_empty(path)) - return false; - - if (path[0] == '/') - return true; - -#if defined(_WIN32) - /* Many roads lead to Rome... - * Note: Drive letter can only be 1 character long */ - return ( string_starts_with_size(path, "\\\\", STRLEN_CONST("\\\\")) - || string_starts_with_size(path + 1, ":/", STRLEN_CONST(":/")) - || string_starts_with_size(path + 1, ":\\", STRLEN_CONST(":\\"))); -#elif defined(__wiiu__) || defined(VITA) + if (!string_is_empty(path)) { - const char *seperator = strchr(path, ':'); - return (seperator && (seperator[1] == '/')); - } + if (path[0] == '/') + return true; +#if defined(_WIN32) + /* Many roads lead to Rome... + * Note: Drive letter can only be 1 character long */ + return ( string_starts_with_size(path, "\\\\", STRLEN_CONST("\\\\")) + || string_starts_with_size(path + 1, ":/", STRLEN_CONST(":/")) + || string_starts_with_size(path + 1, ":\\", STRLEN_CONST(":\\"))); +#elif defined(__wiiu__) || defined(VITA) + { + const char *seperator = strchr(path, ':'); + return (seperator && (seperator[1] == '/')); + } #endif + } return false; } @@ -713,7 +731,7 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks) { #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) #ifdef _WIN32 - char *ret = NULL; + char *ret = NULL; wchar_t *rel_path = utf8_to_utf16_string_alloc(buf); if (rel_path) @@ -767,13 +785,13 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks) { size_t len; /* rebase on working directory */ - if (!getcwd(tmp, PATH_MAX_LENGTH-1)) + if (!getcwd(tmp, PATH_MAX_LENGTH - 1)) return NULL; len = strlen(tmp); - t += len; + t += len; - if (tmp[len-1] != '/') + if (tmp[len - 1] != '/') tmp[t++] = '/'; if (string_is_empty(buf)) @@ -804,8 +822,8 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks) return NULL; /* delete previous segment in tmp by adjusting size t - * tmp[t-1] == '/', find '/' before that */ - t = t-2; + * tmp[t - 1] == '/', find '/' before that */ + t -= 2; while (tmp[t] != '/') t--; t++; @@ -817,7 +835,7 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks) else { /* fail when truncating */ - if (t + next-p+1 > PATH_MAX_LENGTH-1) + if (t + next - p + 1 > PATH_MAX_LENGTH - 1) return NULL; while (p <= next) @@ -936,11 +954,13 @@ void fill_pathname_resolve_relative(char *out_path, size_t fill_pathname_join(char *out_path, const char *dir, const char *path, size_t size) { + size_t _len = 0; if (out_path != dir) - strlcpy(out_path, dir, size); + _len = strlcpy(out_path, dir, size); if (*out_path) - fill_pathname_slash(out_path, size); - return strlcat(out_path, path, size); + _len = fill_pathname_slash(out_path, size); + _len += strlcpy(out_path + _len, path, size - _len); + return _len; } /** @@ -974,18 +994,19 @@ size_t fill_pathname_join_special(char *out_path, /* Try to preserve slash type. */ if (last_slash != (out_path + len - 1)) { - out_path[len] = last_slash[0]; - out_path[len+1] = '\0'; + out_path[ len] = last_slash[0]; + out_path[++len] = '\0'; } } else { - out_path[len] = PATH_DEFAULT_SLASH_C(); - out_path[len+1] = '\0'; + out_path[ len] = PATH_DEFAULT_SLASH_C(); + out_path[++len] = '\0'; } } - return strlcat(out_path, path, size); + len += strlcpy(out_path + len, path, size - len); + return len; } size_t fill_pathname_join_special_ext(char *out_path, @@ -993,12 +1014,12 @@ size_t fill_pathname_join_special_ext(char *out_path, const char *last, const char *ext, size_t size) { - fill_pathname_join(out_path, dir, path, size); + size_t _len = fill_pathname_join(out_path, dir, path, size); if (*out_path) - fill_pathname_slash(out_path, size); - - strlcat(out_path, last, size); - return strlcat(out_path, ext, size); + _len = fill_pathname_slash(out_path, size); + _len += strlcpy(out_path + _len, last, size - _len); + _len += strlcpy(out_path + _len, ext, size - _len); + return _len; } /** @@ -1015,19 +1036,19 @@ size_t fill_pathname_join_special_ext(char *out_path, size_t fill_pathname_join_delim(char *out_path, const char *dir, const char *path, const char delim, size_t size) { - size_t copied; - /* behavior of strlcpy is undefined if dst and src overlap */ + size_t _len; + /* Behavior of strlcpy is undefined if dst and src overlap */ if (out_path == dir) - copied = strlen(dir); + _len = strlen(dir); else - copied = strlcpy(out_path, dir, size); + _len = strlcpy(out_path, dir, size); - out_path[copied] = delim; - out_path[copied+1] = '\0'; + out_path[_len] = delim; + out_path[_len+1] = '\0'; if (path) return strlcat(out_path, path, size); - return copied; + return _len; } size_t fill_pathname_expand_special(char *out_path, @@ -1135,12 +1156,12 @@ size_t fill_pathname_abbreviate_special(char *out_path, * * Leaf function. * - * Changes the slashes to the correct kind for the os + * Changes the slashes to the correct kind for the OS * So forward slash on linux and backslash on Windows **/ void pathname_conform_slashes_to_os(char *path) { - /* Conform slashes to os standard so we get proper matching */ + /* Conform slashes to OS standard so we get proper matching */ char *p; for (p = path; *p; p++) if (*p == '/' || *p == '\\') @@ -1158,7 +1179,7 @@ void pathname_conform_slashes_to_os(char *path) **/ void pathname_make_slashes_portable(char *path) { - /* Conform slashes to os standard so we get proper matching */ + /* Conform slashes to OS standard so we get proper matching */ char *p; for (p = path; *p; p++) if (*p == '/' || *p == '\\') @@ -1334,8 +1355,8 @@ void fill_pathname_application_path(char *s, size_t len) if (realpath(s, resolved_bundle_dir_buf)) { size_t _len = strlcpy(s, resolved_bundle_dir_buf, len - 1); - s[_len ] = '/'; - s[_len+1] = '\0'; + s[ _len] = '/'; + s[++_len] = '\0'; } } #endif diff --git a/src/drivers/libretro/libretro-common/include/file/file_path.h b/src/drivers/libretro/libretro-common/include/file/file_path.h index 5a81aa2..d410cc4 100644 --- a/src/drivers/libretro/libretro-common/include/file/file_path.h +++ b/src/drivers/libretro/libretro-common/include/file/file_path.h @@ -323,10 +323,10 @@ size_t fill_dated_filename(char *out_filename, * Hidden non-leaf function cost: * - Calls time * - Calls rtime_localtime() - * - Calls strlcpy + * - Calls strlcpy 2x * - Calls string_is_empty() * - Calls strftime - * - Calls strlcat at least 2x + * - Calls strlcat * * @return Length of the string copied into @out_path **/ @@ -369,7 +369,7 @@ char *find_last_slash(const char *str); * Hidden non-leaf function cost: * - Calls fill_pathname_slash() * - Calls path_basename() - * - Calls strlcat 2x + * - Calls strlcpy 2x **/ size_t fill_pathname_dir(char *in_dir, const char *in_basename, const char *replace, size_t size); @@ -470,9 +470,8 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath, * between directory and path. * * Hidden non-leaf function cost: - * - calls strlcpy + * - calls strlcpy at least once * - calls fill_pathname_slash() - * - calls strlcat * * Deprecated. Use fill_pathname_join_special() instead * if you can ensure @dir != @out_path @@ -499,9 +498,8 @@ size_t fill_pathname_join(char *out_path, const char *dir, * between directory and path. * * Hidden non-leaf function cost: - * - calls strlcpy + * - calls strlcpy 2x * - calls find_last_slash() - * - calls strlcat * * @return Length of the string copied into @out_path **/ @@ -627,7 +625,7 @@ void path_basedir_wrapper(char *path); * - can call strlcat once if it returns false * - calls strlen **/ -void fill_pathname_slash(char *path, size_t size); +size_t fill_pathname_slash(char *path, size_t size); #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) void fill_pathname_application_path(char *buf, size_t size); @@ -664,6 +662,11 @@ bool path_mkdir(const char *dir); */ bool path_is_directory(const char *path); +/* Time format strings with AM-PM designation require special + * handling due to platform dependence */ +void strftime_am_pm(char *s, size_t len, const char* format, + const void* timeptr); + bool path_is_character_special(const char *path); int path_stat(const char *path); diff --git a/src/drivers/libretro/libretro-common/include/libretro.h b/src/drivers/libretro/libretro-common/include/libretro.h index 3d6df6f..cb1c2d2 100644 --- a/src/drivers/libretro/libretro-common/include/libretro.h +++ b/src/drivers/libretro/libretro-common/include/libretro.h @@ -291,6 +291,7 @@ enum retro_language RETRO_LANGUAGE_CATALAN = 29, RETRO_LANGUAGE_BRITISH_ENGLISH = 30, RETRO_LANGUAGE_HUNGARIAN = 31, + RETRO_LANGUAGE_BELARUSIAN = 32, RETRO_LANGUAGE_LAST, /* Ensure sizeof(enum) == sizeof(int) */ @@ -928,8 +929,6 @@ enum retro_mod * anything else. * It is recommended to expose all relevant pointers through * retro_get_memory_* as well. - * - * Can be called from retro_init and retro_load_game. */ #define RETRO_ENVIRONMENT_SET_GEOMETRY 37 /* const struct retro_game_geometry * -- @@ -1793,6 +1792,63 @@ enum retro_mod * this environment call to query support. */ +#define RETRO_ENVIRONMENT_GET_JIT_CAPABLE 74 + /* bool * -- + * Result is set to true if the frontend has already verified JIT can be + * used, mainly for use iOS/tvOS. On other platforms the result is true. + */ + +#define RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE (75 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* struct retro_microphone_interface * -- + * Returns an interface that can be used to receive input from the microphone driver. + * + * Returns true if microphone support is available, + * even if no microphones are plugged in. + * Returns false if mic support is disabled or unavailable. + * + * This callback can be invoked at any time, + * even before the microphone driver is ready. + */ + +#define RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE 76 + /* const struct retro_netpacket_callback * -- + * When set, a core gains control over network packets sent and + * received during a multiplayer session. This can be used to + * emulate multiplayer games that were originally played on two + * or more separate consoles or computers connected together. + * + * The frontend will take care of connecting players together, + * and the core only needs to send the actual data as needed for + * the emulation, while handshake and connection management happen + * in the background. + * + * When two or more players are connected and this interface has + * been set, time manipulation features (such as pausing, slow motion, + * fast forward, rewinding, save state loading, etc.) are disabled to + * avoid interrupting communication. + * + * Should be set in either retro_init or retro_load_game, but not both. + * + * When not set, a frontend may use state serialization-based + * multiplayer, where a deterministic core supporting multiple + * input devices does not need to take any action on its own. + */ + +#define RETRO_ENVIRONMENT_GET_DEVICE_POWER (77 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* struct retro_device_power * -- + * Returns the device's current power state as reported by the frontend. + * This is useful for emulating the battery level in handheld consoles, + * or for reducing power consumption when on battery power. + * + * The return value indicates whether the frontend can provide this information, + * even if the parameter is NULL. + * + * If the frontend does not support this functionality, + * then the provided argument will remain unchanged. + * + * Note that this environment call describes the power state for the entire device, + * not for individual peripherals like controllers. + */ /* VFS functionality */ @@ -1968,13 +2024,13 @@ struct retro_vfs_interface_info enum retro_hw_render_interface_type { - RETRO_HW_RENDER_INTERFACE_VULKAN = 0, - RETRO_HW_RENDER_INTERFACE_D3D9 = 1, - RETRO_HW_RENDER_INTERFACE_D3D10 = 2, - RETRO_HW_RENDER_INTERFACE_D3D11 = 3, - RETRO_HW_RENDER_INTERFACE_D3D12 = 4, + RETRO_HW_RENDER_INTERFACE_VULKAN = 0, + RETRO_HW_RENDER_INTERFACE_D3D9 = 1, + RETRO_HW_RENDER_INTERFACE_D3D10 = 2, + RETRO_HW_RENDER_INTERFACE_D3D11 = 3, + RETRO_HW_RENDER_INTERFACE_D3D12 = 4, RETRO_HW_RENDER_INTERFACE_GSKIT_PS2 = 5, - RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX + RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX }; /* Base struct. All retro_hw_render_interface_* types @@ -2750,9 +2806,17 @@ enum retro_hw_context_type /* Vulkan, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE. */ RETRO_HW_CONTEXT_VULKAN = 6, - /* Direct3D, set version_major to select the type of interface - * returned by RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */ - RETRO_HW_CONTEXT_DIRECT3D = 7, + /* Direct3D11, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */ + RETRO_HW_CONTEXT_D3D11 = 7, + + /* Direct3D10, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */ + RETRO_HW_CONTEXT_D3D10 = 8, + + /* Direct3D12, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */ + RETRO_HW_CONTEXT_D3D12 = 9, + + /* Direct3D9, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */ + RETRO_HW_CONTEXT_D3D9 = 10, RETRO_HW_CONTEXT_DUMMY = INT_MAX }; @@ -3007,6 +3071,100 @@ struct retro_disk_control_ext_callback retro_get_image_label_t get_image_label; /* Optional - may be NULL */ }; +/* Definitions for RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE. + * A core can set it if sending and receiving custom network packets + * during a multiplayer session is desired. + */ + +/* Netpacket flags for retro_netpacket_send_t */ +#define RETRO_NETPACKET_UNRELIABLE 0 /* Packet to be sent unreliable, depending on network quality it might not arrive. */ +#define RETRO_NETPACKET_RELIABLE (1 << 0) /* Reliable packets are guaranteed to arrive at the target in the order they were send. */ +#define RETRO_NETPACKET_UNSEQUENCED (1 << 1) /* Packet will not be sequenced with other packets and may arrive out of order. Cannot be set on reliable packets. */ + +/* Used by the core to send a packet to one or more connected players. + * A single packet sent via this interface can contain up to 64 KB of data. + * + * The broadcast flag can be set to true to send to multiple connected clients. + * In a broadcast, the client_id argument indicates 1 client NOT to send the + * packet to (pass 0xFFFF to send to everyone). Otherwise, the client_id + * argument indicates a single client to send the packet to. + * + * A frontend must support sending reliable packets (RETRO_NETPACKET_RELIABLE). + * Unreliable packets might not be supported by the frontend, but the flags can + * still be specified. Reliable transmission will be used instead. + * + * If this function is called passing NULL for buf, it will instead flush all + * previously buffered outgoing packets and instantly read any incoming packets. + * During such a call, retro_netpacket_receive_t and retro_netpacket_stop_t can + * be called. The core can perform this in a loop to do a blocking read, i.e., + * wait for incoming data, but needs to handle stop getting called and also + * give up after a short while to avoid freezing on a connection problem. + * + * This function is not guaranteed to be thread-safe and must be called during + * retro_run or any of the netpacket callbacks passed with this interface. + */ +typedef void (RETRO_CALLCONV *retro_netpacket_send_t)(int flags, const void* buf, size_t len, uint16_t client_id, bool broadcast); + +/* Called by the frontend to signify that a multiplayer session has started. + * If client_id is 0 the local player is the host of the session and at this + * point no other player has connected yet. + * + * If client_id is > 0 the local player is a client connected to a host and + * at this point is already fully connected to the host. + * + * The core must store the retro_netpacket_send_t function pointer provided + * here and use it whenever it wants to send a packet. This function pointer + * remains valid until the frontend calls retro_netpacket_stop_t. + */ +typedef void (RETRO_CALLCONV *retro_netpacket_start_t)(uint16_t client_id, retro_netpacket_send_t send_fn); + +/* Called by the frontend when a new packet arrives which has been sent from + * another player with retro_netpacket_send_t. The client_id argument indicates + * who has sent the packet. + */ +typedef void (RETRO_CALLCONV *retro_netpacket_receive_t)(const void* buf, size_t len, uint16_t client_id); + +/* Called by the frontend when the multiplayer session has ended. + * Once this gets called the retro_netpacket_send_t function pointer passed + * to retro_netpacket_start_t will not be valid anymore. + */ +typedef void (RETRO_CALLCONV *retro_netpacket_stop_t)(void); + +/* Called by the frontend every frame (between calls to retro_run while + * updating the state of the multiplayer session. + * This is a good place for the core to call retro_netpacket_send_t from. + */ +typedef void (RETRO_CALLCONV *retro_netpacket_poll_t)(void); + +/* Called by the frontend when a new player connects to the hosted session. + * This is only called on the host side, not for clients connected to the host. + * If this function returns false, the newly connected player gets dropped. + * This can be used for example to limit the number of players. + */ +typedef bool (RETRO_CALLCONV *retro_netpacket_connected_t)(uint16_t client_id); + +/* Called by the frontend when a player leaves or disconnects from the hosted session. + * This is only called on the host side, not for clients connected to the host. + */ +typedef void (RETRO_CALLCONV *retro_netpacket_disconnected_t)(uint16_t client_id); + +/** + * A callback interface for giving a core the ability to send and receive custom + * network packets during a multiplayer session between two or more instances + * of a libretro frontend. + * + * @see RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE + */ +struct retro_netpacket_callback +{ + retro_netpacket_start_t start; + retro_netpacket_receive_t receive; + retro_netpacket_stop_t stop; /* Optional - may be NULL */ + retro_netpacket_poll_t poll; /* Optional - may be NULL */ + retro_netpacket_connected_t connected; /* Optional - may be NULL */ + retro_netpacket_disconnected_t disconnected; /* Optional - may be NULL */ +}; + enum retro_pixel_format { /* 0RGB1555, native endian. @@ -3809,6 +3967,289 @@ struct retro_throttle_state float rate; }; +/** + * Opaque handle to a microphone that's been opened for use. + * The underlying object is accessed or created with \c retro_microphone_interface_t. + */ +typedef struct retro_microphone retro_microphone_t; + +/** + * Parameters for configuring a microphone. + * Some of these might not be honored, + * depending on the available hardware and driver configuration. + */ +typedef struct retro_microphone_params +{ + /** + * The desired sample rate of the microphone's input, in Hz. + * The microphone's input will be resampled, + * so cores can ask for whichever frequency they need. + * + * If zero, some reasonable default will be provided by the frontend + * (usually from its config file). + * + * @see retro_get_mic_rate_t + */ + unsigned rate; +} retro_microphone_params_t; + +/** + * @copydoc retro_microphone_interface::open_mic + */ +typedef retro_microphone_t *(RETRO_CALLCONV *retro_open_mic_t)(const retro_microphone_params_t *params); + +/** + * @copydoc retro_microphone_interface::close_mic + */ +typedef void (RETRO_CALLCONV *retro_close_mic_t)(retro_microphone_t *microphone); + +/** + * @copydoc retro_microphone_interface::get_params + */ +typedef bool (RETRO_CALLCONV *retro_get_mic_params_t)(const retro_microphone_t *microphone, retro_microphone_params_t *params); + +/** + * @copydoc retro_microphone_interface::set_mic_state + */ +typedef bool (RETRO_CALLCONV *retro_set_mic_state_t)(retro_microphone_t *microphone, bool state); + +/** + * @copydoc retro_microphone_interface::get_mic_state + */ +typedef bool (RETRO_CALLCONV *retro_get_mic_state_t)(const retro_microphone_t *microphone); + +/** + * @copydoc retro_microphone_interface::read_mic + */ +typedef int (RETRO_CALLCONV *retro_read_mic_t)(retro_microphone_t *microphone, int16_t* samples, size_t num_samples); + +/** + * The current version of the microphone interface. + * Will be incremented whenever \c retro_microphone_interface or \c retro_microphone_params_t + * receive new fields. + * + * Frontends using cores built against older mic interface versions + * should not access fields introduced in newer versions. + */ +#define RETRO_MICROPHONE_INTERFACE_VERSION 1 + +/** + * An interface for querying the microphone and accessing data read from it. + * + * @see RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE + */ +struct retro_microphone_interface +{ + /** + * The version of this microphone interface. + * Set by the core to request a particular version, + * and set by the frontend to indicate the returned version. + * 0 indicates that the interface is invalid or uninitialized. + */ + unsigned interface_version; + + /** + * Initializes a new microphone. + * Assuming that microphone support is enabled and provided by the frontend, + * cores may call this function whenever necessary. + * A microphone could be opened throughout a core's lifetime, + * or it could wait until a microphone is plugged in to the emulated device. + * + * The returned handle will be valid until it's freed, + * even if the audio driver is reinitialized. + * + * This function is not guaranteed to be thread-safe. + * + * @param args[in] Parameters used to create the microphone. + * May be \c NULL, in which case the default value of each parameter will be used. + * + * @returns Pointer to the newly-opened microphone, + * or \c NULL if one couldn't be opened. + * This likely means that no microphone is plugged in and recognized, + * or the maximum number of supported microphones has been reached. + * + * @note Microphones are \em inactive by default; + * to begin capturing audio, call \c set_mic_state. + * @see retro_microphone_params_t + */ + retro_open_mic_t open_mic; + + /** + * Closes a microphone that was initialized with \c open_mic. + * Calling this function will stop all microphone activity + * and free up the resources that it allocated. + * Afterwards, the handle is invalid and must not be used. + * + * A frontend may close opened microphones when unloading content, + * but this behavior is not guaranteed. + * Cores should close their microphones when exiting, just to be safe. + * + * @param microphone Pointer to the microphone that was allocated by \c open_mic. + * If \c NULL, this function does nothing. + * + * @note The handle might be reused if another microphone is opened later. + */ + retro_close_mic_t close_mic; + + /** + * Returns the configured parameters of this microphone. + * These may differ from what was requested depending on + * the driver and device configuration. + * + * Cores should check these values before they start fetching samples. + * + * Will not change after the mic was opened. + * + * @param microphone[in] Opaque handle to the microphone + * whose parameters will be retrieved. + * @param params[out] The parameters object that the + * microphone's parameters will be copied to. + * + * @return \c true if the parameters were retrieved, + * \c false if there was an error. + */ + retro_get_mic_params_t get_params; + + /** + * Enables or disables the given microphone. + * Microphones are disabled by default + * and must be explicitly enabled before they can be used. + * Disabled microphones will not process incoming audio samples, + * and will therefore have minimal impact on overall performance. + * Cores may enable microphones throughout their lifetime, + * or only for periods where they're needed. + * + * Cores that accept microphone input should be able to operate without it; + * we suggest substituting silence in this case. + * + * @param microphone Opaque handle to the microphone + * whose state will be adjusted. + * This will have been provided by \c open_mic. + * @param state \c true if the microphone should receive audio input, + * \c false if it should be idle. + * @returns \c true if the microphone's state was successfully set, + * \c false if \c microphone is invalid + * or if there was an error. + */ + retro_set_mic_state_t set_mic_state; + + /** + * Queries the active state of a microphone at the given index. + * Will return whether the microphone is enabled, + * even if the driver is paused. + * + * @param microphone Opaque handle to the microphone + * whose state will be queried. + * @return \c true if the provided \c microphone is valid and active, + * \c false if not or if there was an error. + */ + retro_get_mic_state_t get_mic_state; + + /** + * Retrieves the input processed by the microphone since the last call. + * \em Must be called every frame unless \c microphone is disabled, + * similar to how \c retro_audio_sample_batch_t works. + * + * @param[in] microphone Opaque handle to the microphone + * whose recent input will be retrieved. + * @param[out] samples The buffer that will be used to store the microphone's data. + * Microphone input is in mono (i.e. one number per sample). + * Should be large enough to accommodate the expected number of samples per frame; + * for example, a 44.1kHz sample rate at 60 FPS would require space for 735 samples. + * @param[in] num_samples The size of the data buffer in samples (\em not bytes). + * Microphone input is in mono, so a "frame" and a "sample" are equivalent in length here. + * + * @return The number of samples that were copied into \c samples. + * If \c microphone is pending driver initialization, + * this function will copy silence of the requested length into \c samples. + * + * Will return -1 if the microphone is disabled, + * the audio driver is paused, + * or there was an error. + */ + retro_read_mic_t read_mic; +}; + +/** + * Describes how a device is being powered. + * @see RETRO_ENVIRONMENT_GET_DEVICE_POWER + */ +enum retro_power_state +{ + /** + * Indicates that the frontend cannot report its power state at this time, + * most likely due to a lack of support. + * + * \c RETRO_ENVIRONMENT_GET_DEVICE_POWER will not return this value; + * instead, the environment callback will return \c false. + */ + RETRO_POWERSTATE_UNKNOWN = 0, + + /** + * Indicates that the device is running on its battery. + * Usually applies to portable devices such as handhelds, laptops, and smartphones. + */ + RETRO_POWERSTATE_DISCHARGING, + + /** + * Indicates that the device's battery is currently charging. + */ + RETRO_POWERSTATE_CHARGING, + + /** + * Indicates that the device is connected to a power source + * and that its battery has finished charging. + */ + RETRO_POWERSTATE_CHARGED, + + /** + * Indicates that the device is connected to a power source + * and that it does not have a battery. + * This usually suggests a desktop computer or a non-portable game console. + */ + RETRO_POWERSTATE_PLUGGED_IN +}; + +/** + * Indicates that an estimate is not available for the battery level or time remaining, + * even if the actual power state is known. + */ +#define RETRO_POWERSTATE_NO_ESTIMATE (-1) + +/** + * Describes the power state of the device running the frontend. + * @see RETRO_ENVIRONMENT_GET_DEVICE_POWER + */ +struct retro_device_power +{ + /** + * The current state of the frontend's power usage. + */ + enum retro_power_state state; + + /** + * A rough estimate of the amount of time remaining (in seconds) + * before the device powers off. + * This value depends on a variety of factors, + * so it is not guaranteed to be accurate. + * + * Will be set to \c RETRO_POWERSTATE_NO_ESTIMATE if \c state does not equal \c RETRO_POWERSTATE_DISCHARGING. + * May still be set to \c RETRO_POWERSTATE_NO_ESTIMATE if the frontend is unable to provide an estimate. + */ + int seconds; + + /** + * The approximate percentage of battery charge, + * ranging from 0 to 100 (inclusive). + * The device may power off before this reaches 0. + * + * The user might have configured their device + * to stop charging before the battery is full, + * so do not assume that this will be 100 in the \c RETRO_POWERSTATE_CHARGED state. + */ + int8_t percent; +}; + /* Callbacks */ /* Environment callback. Gives implementations a way of performing diff --git a/src/drivers/libretro/libretro-common/include/retro_common_api.h b/src/drivers/libretro/libretro-common/include/retro_common_api.h index 0f68b7d..ebb645d 100644 --- a/src/drivers/libretro/libretro-common/include/retro_common_api.h +++ b/src/drivers/libretro/libretro-common/include/retro_common_api.h @@ -101,6 +101,26 @@ typedef int ssize_t; #define STRING_REP_UINT64 "%" PRIu64 #define STRING_REP_USIZE "%" PRIuPTR +/* Wrap a declaration in RETRO_DEPRECATED() to produce a compiler warning when +it's used. This is intended for developer machines, so it won't work on ancient +or obscure compilers */ +#if defined(_MSC_VER) +#if _MSC_VER >= 1400 /* Visual C 2005 or later */ +#define RETRO_DEPRECATED(decl) __declspec(deprecated) decl +#endif +#elif defined(__GNUC__) +#if __GNUC__ >= 3 /* GCC 3 or later */ +#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated)) +#endif +#elif defined(__clang__) +#if __clang_major__ >= 3 /* clang 3 or later */ +#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated)) +#endif +#endif +#ifndef RETRO_DEPRECATED /* Unsupported compilers */ +#define RETRO_DEPRECATED(decl) decl +#endif + /* I would like to see retro_inline.h moved in here; possibly boolean too. diff --git a/src/drivers/libretro/libretro-common/include/retro_miscellaneous.h b/src/drivers/libretro/libretro-common/include/retro_miscellaneous.h index a3d2854..953ab18 100644 --- a/src/drivers/libretro/libretro-common/include/retro_miscellaneous.h +++ b/src/drivers/libretro/libretro-common/include/retro_miscellaneous.h @@ -49,6 +49,10 @@ #include #endif +#ifdef IOS +#include +#endif + static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count) { uint32_t i; diff --git a/src/drivers/libretro/libretro-common/include/string/stdstring.h b/src/drivers/libretro/libretro-common/include/string/stdstring.h index 79fa409..4e38e8d 100644 --- a/src/drivers/libretro/libretro-common/include/string/stdstring.h +++ b/src/drivers/libretro/libretro-common/include/string/stdstring.h @@ -48,7 +48,7 @@ RETRO_BEGIN_DECLS #define TOUPPER(c) ((c) & ~(lr_char_props[(unsigned char)(c)] & 0x20)) /* C standard says \f \v are space, but this one disagrees */ -#define ISSPACE(c) (lr_char_props[(unsigned char)(c)] & 0x80) +#define ISSPACE(c) (lr_char_props[(unsigned char)(c)] & 0x80) #define ISDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x40) #define ISALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x20) @@ -204,7 +204,7 @@ char *string_trim_whitespace(char *const s); * correctly any text containing so-called 'wide' Unicode * characters (e.g. CJK languages, emojis, etc.). **/ -void word_wrap(char *dst, size_t dst_size, const char *src, size_t src_len, +size_t word_wrap(char *dst, size_t dst_size, const char *src, size_t src_len, int line_width, int wideglyph_width, unsigned max_lines); /** @@ -241,7 +241,7 @@ void word_wrap(char *dst, size_t dst_size, const char *src, size_t src_len, * on-screen pixel width deviates greatly from the set * @wideglyph_width value. **/ -void word_wrap_wideglyph( +size_t word_wrap_wideglyph( char *dst, size_t dst_size, const char *src, size_t src_len, int line_width, int wideglyph_width, @@ -331,7 +331,7 @@ int string_count_occurrences_single_character(const char *str, char c); /** * string_replace_whitespace_with_single_character: - * + * * Leaf function. * * Replaces all spaces with given character @c. diff --git a/src/drivers/libretro/libretro-common/streams/file_stream.c b/src/drivers/libretro/libretro-common/streams/file_stream.c index 3956f08..befb31c 100644 --- a/src/drivers/libretro/libretro-common/streams/file_stream.c +++ b/src/drivers/libretro/libretro-common/streams/file_stream.c @@ -221,7 +221,7 @@ int filestream_vscanf(RFILE *stream, const char* format, va_list *args) va_list args_copy; const char *bufiter = buf; int ret = 0; - int64_t startpos = 0; + int64_t startpos = filestream_tell(stream); int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1); if (maxlen <= 0) @@ -330,7 +330,6 @@ int filestream_vscanf(RFILE *stream, const char* format, va_list *args) } va_end(args_copy); - startpos = filestream_tell(stream); filestream_seek(stream, startpos + (bufiter - buf), RETRO_VFS_SEEK_POSITION_START); diff --git a/src/drivers/libretro/libretro-common/string/stdstring.c b/src/drivers/libretro/libretro-common/string/stdstring.c index 4b36deb..7f05883 100644 --- a/src/drivers/libretro/libretro-common/string/stdstring.c +++ b/src/drivers/libretro/libretro-common/string/stdstring.c @@ -217,27 +217,24 @@ char *string_trim_whitespace(char *const s) * correctly any text containing so-called 'wide' Unicode * characters (e.g. CJK languages, emojis, etc.). **/ -void word_wrap( +size_t word_wrap( char *dst, size_t dst_size, const char *src, size_t src_len, int line_width, int wideglyph_width, unsigned max_lines) { - char *lastspace = NULL; + char *last_space = NULL; unsigned counter = 0; unsigned lines = 1; const char *src_end = src + src_len; /* Prevent buffer overflow */ if (dst_size < src_len + 1) - return; + return 0; /* Early return if src string length is less * than line width */ if (src_len < (size_t)line_width) - { - strlcpy(dst, src, dst_size); - return; - } + return strlcpy(dst, src, dst_size); while (*src != '\0') { @@ -245,21 +242,18 @@ void word_wrap( counter++; if (*src == ' ') - lastspace = dst; /* Remember the location of the whitespace */ + last_space = dst; /* Remember the location of the whitespace */ else if (*src == '\n') { /* If newlines embedded in the input, * reset the index */ lines++; - counter = 0; + counter = 0; /* Early return if remaining src string * length is less than line width */ if (src_end - src <= line_width) - { - strlcpy(dst, src, dst_size); - return; - } + return strlcpy(dst, src, dst_size); } while (char_len--) @@ -269,29 +263,27 @@ void word_wrap( { counter = 0; - if (lastspace && (max_lines == 0 || lines < max_lines)) + if (last_space && (max_lines == 0 || lines < max_lines)) { /* Replace nearest (previous) whitespace * with newline character */ - *lastspace = '\n'; + *last_space = '\n'; lines++; - src -= dst - lastspace - 1; - dst = lastspace + 1; - lastspace = NULL; + src -= dst - last_space - 1; + dst = last_space + 1; + last_space = NULL; /* Early return if remaining src string * length is less than line width */ if (src_end - src < line_width) - { - strlcpy(dst, src, dst_size); - return; - } + return strlcpy(dst, src, dst_size); } } } *dst = '\0'; + return 0; } /** @@ -327,7 +319,7 @@ void word_wrap( * on-screen pixel width deviates greatly from the set * @wideglyph_width value. **/ -void word_wrap_wideglyph(char *dst, size_t dst_size, +size_t word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, size_t src_len, int line_width, int wideglyph_width, unsigned max_lines) { @@ -359,14 +351,11 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, unsigned counter_normalized = 0; int line_width_normalized = line_width * 100; int additional_counter_normalized = wideglyph_width - 100; - + /* Early return if src string length is less * than line width */ if (src_end - src < line_width) - { - strlcpy(dst, src, dst_size); - return; - } + return strlcpy(dst, src, dst_size); while (*src != '\0') { @@ -389,10 +378,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, /* Early return if remaining src string * length is less than line width */ if (src_end - src <= line_width) - { - strlcpy(dst, src, dst_size); - return; - } + return strlcpy(dst, src, dst_size); } else if (char_len >= 3) { @@ -424,10 +410,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, /* Early return if remaining src string * length is less than line width */ if (src_end - src <= line_width) - { - strlcpy(dst, src, dst_size); - return; - } + return strlcpy(dst, src, dst_size); } else if (lastspace) { @@ -442,15 +425,13 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, /* Early return if remaining src string * length is less than line width */ if (src_end - src < line_width) - { - strlcpy(dst, src, dst_size); - return; - } + return strlcpy(dst, src, dst_size); } } } *dst = '\0'; + return 0; } /** @@ -592,7 +573,7 @@ unsigned string_hex_to_unsigned(const char *str) if (str[0] != '\0' && str[1] != '\0') { if ( (str[0] == '0') && - ((str[1] == 'x') || + ((str[1] == 'x') || (str[1] == 'X'))) { hex_str = str + 2; @@ -635,7 +616,7 @@ int string_count_occurrences_single_character(const char *str, char c) /** * string_replace_whitespace_with_single_character: - * + * * Leaf function. * * Replaces all spaces with given character @c. diff --git a/src/drivers/libretro/libretro-common/vfs/vfs_implementation.c b/src/drivers/libretro/libretro-common/vfs/vfs_implementation.c index a84569a..2d958fe 100644 --- a/src/drivers/libretro/libretro-common/vfs/vfs_implementation.c +++ b/src/drivers/libretro/libretro-common/vfs/vfs_implementation.c @@ -301,7 +301,7 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( { int flags = 0; const char *mode_str = NULL; - libretro_vfs_implementation_file *stream = + libretro_vfs_implementation_file *stream = (libretro_vfs_implementation_file*) malloc(sizeof(*stream)); @@ -452,14 +452,14 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( * * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html * - * If the size argument is not zero but buf is NULL, + * If the size argument is not zero but buf is NULL, * a buffer of the given size will be allocated immediately, and * released on close. This is an extension to ANSI C. * - * Since C89 does not support specifying a NULL buffer + * Since C89 does not support specifying a NULL buffer * with a non-zero size, we create and track our own buffer for it. */ - /* TODO: this is only useful for a few platforms, + /* TODO: this is only useful for a few platforms, * find which and add ifdef */ #if defined(_3DS) if (stream->scheme != VFS_SCHEME_CDROM) @@ -648,7 +648,7 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) #ifdef HAVE_MMAP /* Need to check stream->mapped because this function * is called in filestream_open() */ - if (stream->mapped && stream->hints & + if (stream->mapped && stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) return stream->mappos; #endif @@ -699,8 +699,8 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len) { - int64_t pos = 0; - size_t result = -1; + int64_t pos = 0; + ssize_t result = -1; if (!stream) return -1; @@ -738,39 +738,40 @@ int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream) int retro_vfs_file_remove_impl(const char *path) { + if (path && *path) + { + int ret = -1; #if defined(_WIN32) && !defined(_XBOX) - /* Win32 (no Xbox) */ - + /* Win32 (no Xbox) */ #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 - char *path_local = NULL; + char *path_local = NULL; + if ((path_local = utf8_to_local_string_alloc(path))) + { + /* We need to check if path is a directory */ + if ((retro_vfs_stat_impl(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0) + ret = _rmdir(path_local); + else + ret = remove(path_local); + free(path_local); + } #else - wchar_t *path_wide = NULL; + wchar_t *path_wide = NULL; + if ((path_wide = utf8_to_utf16_string_alloc(path))) + { + /* We need to check if path is a directory */ + if ((retro_vfs_stat_impl(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0) + ret = _wrmdir(path_wide); + else + ret = _wremove(path_wide); + free(path_wide); + } +#endif +#else + ret = remove(path); #endif - if (!path || !*path) - return -1; -#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 - if ((path_local = utf8_to_local_string_alloc(path))) - { - int ret = remove(path_local); - free(path_local); - if (ret == 0) return 0; } -#else - if ((path_wide = utf8_to_utf16_string_alloc(path))) - { - int ret = _wremove(path_wide); - free(path_wide); - - if (ret == 0) - return 0; - } -#endif -#else - if (remove(path) == 0) - return 0; -#endif return -1; } @@ -842,132 +843,118 @@ const char *retro_vfs_file_get_path_impl( int retro_vfs_stat_impl(const char *path, int32_t *size) { - bool is_dir = false; - bool is_character_special = false; + int ret = RETRO_VFS_STAT_IS_VALID; + + if (!path || !*path) + return 0; + { #if defined(VITA) - /* Vita / PSP */ - SceIoStat buf; - int dir_ret; - char *tmp = NULL; - size_t len = 0; + /* Vita / PSP */ + SceIoStat stat_buf; + int dir_ret; + char *tmp = strdup(path); + size_t len = strlen(tmp); + if (tmp[len-1] == '/') + tmp[len-1] = '\0'; - if (!path || !*path) - return 0; + dir_ret = sceIoGetstat(tmp, &stat_buf); + free(tmp); + if (dir_ret < 0) + return 0; - tmp = strdup(path); - len = strlen(tmp); - if (tmp[len-1] == '/') - tmp[len-1] = '\0'; + if (size) + *size = (int32_t)stat_buf.st_size; - dir_ret = sceIoGetstat(tmp, &buf); - free(tmp); - if (dir_ret < 0) - return 0; - - if (size) - *size = (int32_t)buf.st_size; - - is_dir = FIO_S_ISDIR(buf.st_mode); + if (FIO_S_ISDIR(stat_buf.st_mode)) + ret |= RETRO_VFS_STAT_IS_DIRECTORY; #elif defined(__PSL1GHT__) || defined(__PS3__) - /* Lowlevel Lv2 */ - sysFSStat buf; + /* Lowlevel Lv2 */ + sysFSStat stat_buf; - if (!path || !*path) - return 0; - if (sysFsStat(path, &buf) < 0) - return 0; + if (sysFsStat(path, &stat_buf) < 0) + return 0; - if (size) - *size = (int32_t)buf.st_size; + if (size) + *size = (int32_t)stat_buf.st_size; - is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR); + if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) + ret |= RETRO_VFS_STAT_IS_DIRECTORY; #elif defined(_WIN32) - /* Windows */ - DWORD file_info; - struct _stat buf; + /* Windows */ + struct _stat stat_buf; #if defined(LEGACY_WIN32) - char *path_local = NULL; + char *path_local = utf8_to_local_string_alloc(path); + DWORD file_info = GetFileAttributes(path_local); + + if (!string_is_empty(path_local)) + _stat(path_local, &stat_buf); + + if (path_local) + free(path_local); #else - wchar_t *path_wide = NULL; + wchar_t *path_wide = utf8_to_utf16_string_alloc(path); + DWORD file_info = GetFileAttributesW(path_wide); + + _wstat(path_wide, &stat_buf); + + if (path_wide) + free(path_wide); #endif + if (file_info == INVALID_FILE_ATTRIBUTES) + return 0; - if (!path || !*path) - return 0; - -#if defined(LEGACY_WIN32) - path_local = utf8_to_local_string_alloc(path); - file_info = GetFileAttributes(path_local); - - if (!string_is_empty(path_local)) - _stat(path_local, &buf); - - if (path_local) - free(path_local); -#else - path_wide = utf8_to_utf16_string_alloc(path); - file_info = GetFileAttributesW(path_wide); - - _wstat(path_wide, &buf); - - if (path_wide) - free(path_wide); -#endif - - if (file_info == INVALID_FILE_ATTRIBUTES) - return 0; - - if (size) - *size = (int32_t)buf.st_size; - - is_dir = (file_info & FILE_ATTRIBUTE_DIRECTORY); + if (size) + *size = (int32_t)stat_buf.st_size; + if (file_info & FILE_ATTRIBUTE_DIRECTORY) + ret |= RETRO_VFS_STAT_IS_DIRECTORY; #elif defined(GEKKO) - /* On GEKKO platforms, paths cannot have - * trailing slashes - we must therefore - * remove them */ - char *path_buf = NULL; - int stat_ret = -1; - struct stat stat_buf; - size_t len; + /* On GEKKO platforms, paths cannot have + * trailing slashes - we must therefore + * remove them */ + size_t len; + char *path_buf = NULL; + struct stat stat_buf; - if (string_is_empty(path)) - return 0; + if (!(path_buf = strdup(path))) + return 0; - if (!(path_buf = strdup(path))) - return 0; + if ((len = strlen(path_buf)) > 0) + if (path_buf[len - 1] == '/') + path_buf[len - 1] = '\0'; - if ((len = strlen(path_buf)) > 0) - if (path_buf[len - 1] == '/') - path_buf[len - 1] = '\0'; + if (stat(path_buf, &stat_buf) < 0) + { + free(path_buf); + return 0; + } - stat_ret = stat(path_buf, &stat_buf); - free(path_buf); - - if (stat_ret < 0) - return 0; - - if (size) - *size = (int32_t)stat_buf.st_size; - - is_dir = S_ISDIR(stat_buf.st_mode); - is_character_special = S_ISCHR(stat_buf.st_mode); + free(path_buf); + + if (size) + *size = (int32_t)stat_buf.st_size; + if (S_ISDIR(stat_buf.st_mode)) + ret |= RETRO_VFS_STAT_IS_DIRECTORY; + if (S_ISCHR(stat_buf.st_mode)) + ret |= RETRO_VFS_STAT_IS_CHARACTER_SPECIAL; #else - /* Every other platform */ - struct stat buf; + /* Every other platform */ + struct stat stat_buf; - if (!path || !*path) - return 0; - if (stat(path, &buf) < 0) - return 0; + if (stat(path, &stat_buf) < 0) + return 0; - if (size) - *size = (int32_t)buf.st_size; + if (size) + *size = (int32_t)stat_buf.st_size; - is_dir = S_ISDIR(buf.st_mode); - is_character_special = S_ISCHR(buf.st_mode); + if (S_ISDIR(stat_buf.st_mode)) + ret |= RETRO_VFS_STAT_IS_DIRECTORY; + if (S_ISCHR(stat_buf.st_mode)) + ret |= RETRO_VFS_STAT_IS_CHARACTER_SPECIAL; #endif - return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0); + } + return ret; } #if defined(VITA)