Updated libretro-common (#609)

This commit is contained in:
Alberto Fustinoni 2024-06-29 07:41:51 +09:00 committed by GitHub
parent 819805ea75
commit 9e685cda13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 758 additions and 302 deletions

View File

@ -24,6 +24,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <locale.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -68,6 +69,31 @@
#endif #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 * Create a new linked list with one node in it
* The path on this node will be set to NULL * 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); string_to_lower(buf);
/* Check if this is a '.zip', '.apk' or '.7z' file */ /* Check if this is a '.zip', '.apk' or '.7z' file */
if (string_is_equal(buf, ".zip") || if ( string_is_equal(buf, ".zip")
string_is_equal(buf, ".apk") || || string_is_equal(buf, ".apk")
string_is_equal(buf + 1, ".7z")) || string_is_equal(buf + 1, ".7z"))
return delim; return delim;
} }
else if (delim - path > 3) 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 *path_get_extension(const char *path)
{ {
const char *ext; 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 ext + 1;
return ""; return "";
} }
@ -228,7 +254,7 @@ const char *path_get_extension(const char *path)
char *path_get_extension_mutable(const char *path) char *path_get_extension_mutable(const char *path)
{ {
char *ext = NULL; 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 ext;
return NULL; 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, size_t fill_pathname(char *out_path, const char *in_path,
const char *replace, size_t size) const char *replace, size_t size)
{ {
size_t _len;
char tmp_path[PATH_MAX_LENGTH]; char tmp_path[PATH_MAX_LENGTH];
char *tok = NULL; char *tok = NULL;
strlcpy(tmp_path, in_path, sizeof(tmp_path)); strlcpy(tmp_path, in_path, sizeof(tmp_path));
if ((tok = (char*)strrchr(path_basename(tmp_path), '.'))) if ((tok = (char*)strrchr(path_basename(tmp_path), '.')))
*tok = '\0'; *tok = '\0';
strlcpy(out_path, tmp_path, size); _len = strlcpy(out_path, tmp_path, size);
return strlcat(out_path, replace, 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 * @size : size of path
* *
* Find last slash in path. Tries to find * Find last slash in path. Tries to find
* a backslash on Windows too which takes precedence * a backslash as used for Windows paths,
* over regular slash. * otherwise checks for a regular slash.
* @return pointer to last slash/backslash found in @str. * @return pointer to last slash/backslash found in @str.
**/ **/
char *find_last_slash(const char *str) char *find_last_slash(const char *str)
{ {
const char *slash = strrchr(str, '/'); const char *slash = strrchr(str, '/');
#ifdef _WIN32
const char *backslash = strrchr(str, '\\'); const char *backslash = strrchr(str, '\\');
if (!slash || (backslash > slash)) if (!slash || (backslash > slash))
return (char*)backslash; return (char*)backslash;
#endif else
return (char*)slash; return (char*)slash;
} }
/** /**
@ -344,24 +371,20 @@ char *find_last_slash(const char *str)
* Assumes path is a directory. Appends a slash * Assumes path is a directory. Appends a slash
* if not already there. * 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; size_t path_len;
const char *last_slash = find_last_slash(path); const char *last_slash = find_last_slash(path);
if (!last_slash) if (!last_slash)
{ return strlcat(path, PATH_DEFAULT_SLASH(), size);
strlcat(path, PATH_DEFAULT_SLASH(), size); path_len = strlen(path);
return;
}
path_len = strlen(path);
/* Try to preserve slash type. */ /* Try to preserve slash type. */
if (last_slash != (path + path_len - 1)) if (last_slash != (path + path_len - 1))
{ {
path[path_len] = last_slash[0]; path[ path_len] = last_slash[0];
path[path_len+1] = '\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, size_t fill_pathname_dir(char *in_dir, const char *in_basename,
const char *replace, size_t size) const char *replace, size_t size)
{ {
const char *base = NULL; size_t _len = fill_pathname_slash(in_dir, size);
const char *base = path_basename(in_basename);
fill_pathname_slash(in_dir, size); _len += strlcpy(in_dir + _len, base, size - _len);
base = path_basename(in_basename); _len += strlcpy(in_dir + _len, replace, size - _len);
strlcat(in_dir, base, size); return _len;
return strlcat(in_dir, replace, size);
} }
/** /**
@ -514,14 +536,14 @@ void fill_pathname_parent_dir(char *out_dir,
size_t fill_dated_filename(char *out_filename, size_t fill_dated_filename(char *out_filename,
const char *ext, size_t size) const char *ext, size_t size)
{ {
time_t cur_time = time(NULL); size_t _len;
struct tm tm_; struct tm tm_;
time_t cur_time = time(NULL);
rtime_localtime(&cur_time, &tm_); rtime_localtime(&cur_time, &tm_);
_len = strftime(out_filename, size,
strftime(out_filename, size,
"RetroArch-%m%d-%H%M%S", &tm_); "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, size_t fill_str_dated_filename(char *out_filename,
const char *in_str, const char *ext, size_t size) const char *in_str, const char *ext, size_t size)
{ {
char format[NAME_MAX_LENGTH];
struct tm tm_; struct tm tm_;
char format[NAME_MAX_LENGTH];
size_t _len = 0;
time_t cur_time = time(NULL); time_t cur_time = time(NULL);
rtime_localtime(&cur_time, &tm_); rtime_localtime(&cur_time, &tm_);
_len = strlcpy(out_filename, in_str, size);
strlcpy(out_filename, in_str, size);
if (string_is_empty(ext)) if (string_is_empty(ext))
{ {
strftime(format, sizeof(format), "-%y%m%d-%H%M%S", &tm_); 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_); else
strlcat(out_filename, format, size); {
return strlcat(out_filename, ext, size); 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) const char *path_basename(const char *path)
{ {
/* We cut at the first compression-related hash */ /* We cut either at the first compression-related hash,
const char *delim = path_get_archive_delim(path); * or we cut at the last slash */
if (delim) const char *ptr = NULL;
return delim + 1; if ( (ptr = path_get_archive_delim(path))
|| (ptr = find_last_slash(path)))
{ return ptr + 1;
/* We cut at the last slash */
const char *last = find_last_slash(path);
if (last)
return last + 1;
}
return path; return path;
} }
@ -670,24 +689,23 @@ const char *path_basename_nocompression(const char *path)
**/ **/
bool path_is_absolute(const char *path) bool path_is_absolute(const char *path)
{ {
if (string_is_empty(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)
{ {
const char *seperator = strchr(path, ':'); if (path[0] == '/')
return (seperator && (seperator[1] == '/')); 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 #endif
}
return false; 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) #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
#ifdef _WIN32 #ifdef _WIN32
char *ret = NULL; char *ret = NULL;
wchar_t *rel_path = utf8_to_utf16_string_alloc(buf); wchar_t *rel_path = utf8_to_utf16_string_alloc(buf);
if (rel_path) if (rel_path)
@ -767,13 +785,13 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
{ {
size_t len; size_t len;
/* rebase on working directory */ /* rebase on working directory */
if (!getcwd(tmp, PATH_MAX_LENGTH-1)) if (!getcwd(tmp, PATH_MAX_LENGTH - 1))
return NULL; return NULL;
len = strlen(tmp); len = strlen(tmp);
t += len; t += len;
if (tmp[len-1] != '/') if (tmp[len - 1] != '/')
tmp[t++] = '/'; tmp[t++] = '/';
if (string_is_empty(buf)) if (string_is_empty(buf))
@ -804,8 +822,8 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
return NULL; return NULL;
/* delete previous segment in tmp by adjusting size t /* delete previous segment in tmp by adjusting size t
* tmp[t-1] == '/', find '/' before that */ * tmp[t - 1] == '/', find '/' before that */
t = t-2; t -= 2;
while (tmp[t] != '/') while (tmp[t] != '/')
t--; t--;
t++; t++;
@ -817,7 +835,7 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
else else
{ {
/* fail when truncating */ /* fail when truncating */
if (t + next-p+1 > PATH_MAX_LENGTH-1) if (t + next - p + 1 > PATH_MAX_LENGTH - 1)
return NULL; return NULL;
while (p <= next) while (p <= next)
@ -936,11 +954,13 @@ void fill_pathname_resolve_relative(char *out_path,
size_t fill_pathname_join(char *out_path, size_t fill_pathname_join(char *out_path,
const char *dir, const char *path, size_t size) const char *dir, const char *path, size_t size)
{ {
size_t _len = 0;
if (out_path != dir) if (out_path != dir)
strlcpy(out_path, dir, size); _len = strlcpy(out_path, dir, size);
if (*out_path) if (*out_path)
fill_pathname_slash(out_path, size); _len = fill_pathname_slash(out_path, size);
return strlcat(out_path, 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. */ /* Try to preserve slash type. */
if (last_slash != (out_path + len - 1)) if (last_slash != (out_path + len - 1))
{ {
out_path[len] = last_slash[0]; out_path[ len] = last_slash[0];
out_path[len+1] = '\0'; out_path[++len] = '\0';
} }
} }
else else
{ {
out_path[len] = PATH_DEFAULT_SLASH_C(); out_path[ len] = PATH_DEFAULT_SLASH_C();
out_path[len+1] = '\0'; 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, 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, const char *last, const char *ext,
size_t size) 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) if (*out_path)
fill_pathname_slash(out_path, size); _len = fill_pathname_slash(out_path, size);
_len += strlcpy(out_path + _len, last, size - _len);
strlcat(out_path, last, size); _len += strlcpy(out_path + _len, ext, size - _len);
return strlcat(out_path, ext, size); 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, size_t fill_pathname_join_delim(char *out_path, const char *dir,
const char *path, const char delim, size_t size) const char *path, const char delim, size_t size)
{ {
size_t copied; size_t _len;
/* behavior of strlcpy is undefined if dst and src overlap */ /* Behavior of strlcpy is undefined if dst and src overlap */
if (out_path == dir) if (out_path == dir)
copied = strlen(dir); _len = strlen(dir);
else else
copied = strlcpy(out_path, dir, size); _len = strlcpy(out_path, dir, size);
out_path[copied] = delim; out_path[_len] = delim;
out_path[copied+1] = '\0'; out_path[_len+1] = '\0';
if (path) if (path)
return strlcat(out_path, path, size); return strlcat(out_path, path, size);
return copied; return _len;
} }
size_t fill_pathname_expand_special(char *out_path, size_t fill_pathname_expand_special(char *out_path,
@ -1135,12 +1156,12 @@ size_t fill_pathname_abbreviate_special(char *out_path,
* *
* Leaf function. * 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 * So forward slash on linux and backslash on Windows
**/ **/
void pathname_conform_slashes_to_os(char *path) 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; char *p;
for (p = path; *p; p++) for (p = path; *p; p++)
if (*p == '/' || *p == '\\') if (*p == '/' || *p == '\\')
@ -1158,7 +1179,7 @@ void pathname_conform_slashes_to_os(char *path)
**/ **/
void pathname_make_slashes_portable(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; char *p;
for (p = path; *p; p++) for (p = path; *p; p++)
if (*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)) if (realpath(s, resolved_bundle_dir_buf))
{ {
size_t _len = strlcpy(s, resolved_bundle_dir_buf, len - 1); size_t _len = strlcpy(s, resolved_bundle_dir_buf, len - 1);
s[_len ] = '/'; s[ _len] = '/';
s[_len+1] = '\0'; s[++_len] = '\0';
} }
} }
#endif #endif

View File

@ -323,10 +323,10 @@ size_t fill_dated_filename(char *out_filename,
* Hidden non-leaf function cost: * Hidden non-leaf function cost:
* - Calls time * - Calls time
* - Calls rtime_localtime() * - Calls rtime_localtime()
* - Calls strlcpy * - Calls strlcpy 2x
* - Calls string_is_empty() * - Calls string_is_empty()
* - Calls strftime * - Calls strftime
* - Calls strlcat at least 2x * - Calls strlcat
* *
* @return Length of the string copied into @out_path * @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: * Hidden non-leaf function cost:
* - Calls fill_pathname_slash() * - Calls fill_pathname_slash()
* - Calls path_basename() * - Calls path_basename()
* - Calls strlcat 2x * - Calls strlcpy 2x
**/ **/
size_t fill_pathname_dir(char *in_dir, const char *in_basename, size_t fill_pathname_dir(char *in_dir, const char *in_basename,
const char *replace, size_t size); 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. * between directory and path.
* *
* Hidden non-leaf function cost: * Hidden non-leaf function cost:
* - calls strlcpy * - calls strlcpy at least once
* - calls fill_pathname_slash() * - calls fill_pathname_slash()
* - calls strlcat
* *
* Deprecated. Use fill_pathname_join_special() instead * Deprecated. Use fill_pathname_join_special() instead
* if you can ensure @dir != @out_path * 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. * between directory and path.
* *
* Hidden non-leaf function cost: * Hidden non-leaf function cost:
* - calls strlcpy * - calls strlcpy 2x
* - calls find_last_slash() * - calls find_last_slash()
* - calls strlcat
* *
* @return Length of the string copied into @out_path * @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 * - can call strlcat once if it returns false
* - calls strlen * - 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) #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
void fill_pathname_application_path(char *buf, size_t size); 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); 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); bool path_is_character_special(const char *path);
int path_stat(const char *path); int path_stat(const char *path);

View File

@ -291,6 +291,7 @@ enum retro_language
RETRO_LANGUAGE_CATALAN = 29, RETRO_LANGUAGE_CATALAN = 29,
RETRO_LANGUAGE_BRITISH_ENGLISH = 30, RETRO_LANGUAGE_BRITISH_ENGLISH = 30,
RETRO_LANGUAGE_HUNGARIAN = 31, RETRO_LANGUAGE_HUNGARIAN = 31,
RETRO_LANGUAGE_BELARUSIAN = 32,
RETRO_LANGUAGE_LAST, RETRO_LANGUAGE_LAST,
/* Ensure sizeof(enum) == sizeof(int) */ /* Ensure sizeof(enum) == sizeof(int) */
@ -928,8 +929,6 @@ enum retro_mod
* anything else. * anything else.
* It is recommended to expose all relevant pointers through * It is recommended to expose all relevant pointers through
* retro_get_memory_* as well. * retro_get_memory_* as well.
*
* Can be called from retro_init and retro_load_game.
*/ */
#define RETRO_ENVIRONMENT_SET_GEOMETRY 37 #define RETRO_ENVIRONMENT_SET_GEOMETRY 37
/* const struct retro_game_geometry * -- /* const struct retro_game_geometry * --
@ -1793,6 +1792,63 @@ enum retro_mod
* this environment call to query support. * 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 */ /* VFS functionality */
@ -1968,13 +2024,13 @@ struct retro_vfs_interface_info
enum retro_hw_render_interface_type enum retro_hw_render_interface_type
{ {
RETRO_HW_RENDER_INTERFACE_VULKAN = 0, RETRO_HW_RENDER_INTERFACE_VULKAN = 0,
RETRO_HW_RENDER_INTERFACE_D3D9 = 1, RETRO_HW_RENDER_INTERFACE_D3D9 = 1,
RETRO_HW_RENDER_INTERFACE_D3D10 = 2, RETRO_HW_RENDER_INTERFACE_D3D10 = 2,
RETRO_HW_RENDER_INTERFACE_D3D11 = 3, RETRO_HW_RENDER_INTERFACE_D3D11 = 3,
RETRO_HW_RENDER_INTERFACE_D3D12 = 4, RETRO_HW_RENDER_INTERFACE_D3D12 = 4,
RETRO_HW_RENDER_INTERFACE_GSKIT_PS2 = 5, 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 /* 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. */ /* Vulkan, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE. */
RETRO_HW_CONTEXT_VULKAN = 6, RETRO_HW_CONTEXT_VULKAN = 6,
/* Direct3D, set version_major to select the type of interface /* Direct3D11, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */
* returned by RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */ RETRO_HW_CONTEXT_D3D11 = 7,
RETRO_HW_CONTEXT_DIRECT3D = 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 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 */ 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 enum retro_pixel_format
{ {
/* 0RGB1555, native endian. /* 0RGB1555, native endian.
@ -3809,6 +3967,289 @@ struct retro_throttle_state
float rate; 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 */ /* Callbacks */
/* Environment callback. Gives implementations a way of performing /* Environment callback. Gives implementations a way of performing

View File

@ -101,6 +101,26 @@ typedef int ssize_t;
#define STRING_REP_UINT64 "%" PRIu64 #define STRING_REP_UINT64 "%" PRIu64
#define STRING_REP_USIZE "%" PRIuPTR #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. I would like to see retro_inline.h moved in here; possibly boolean too.

View File

@ -49,6 +49,10 @@
#include <compat/msvc.h> #include <compat/msvc.h>
#endif #endif
#ifdef IOS
#include <sys/param.h>
#endif
static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count) static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count)
{ {
uint32_t i; uint32_t i;

View File

@ -48,7 +48,7 @@ RETRO_BEGIN_DECLS
#define TOUPPER(c) ((c) & ~(lr_char_props[(unsigned char)(c)] & 0x20)) #define TOUPPER(c) ((c) & ~(lr_char_props[(unsigned char)(c)] & 0x20))
/* C standard says \f \v are space, but this one disagrees */ /* 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 ISDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x40)
#define ISALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x20) #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 * correctly any text containing so-called 'wide' Unicode
* characters (e.g. CJK languages, emojis, etc.). * 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); 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 * on-screen pixel width deviates greatly from the set
* @wideglyph_width value. * @wideglyph_width value.
**/ **/
void word_wrap_wideglyph( size_t word_wrap_wideglyph(
char *dst, size_t dst_size, char *dst, size_t dst_size,
const char *src, size_t src_len, const char *src, size_t src_len,
int line_width, int wideglyph_width, 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: * string_replace_whitespace_with_single_character:
* *
* Leaf function. * Leaf function.
* *
* Replaces all spaces with given character @c. * Replaces all spaces with given character @c.

View File

@ -221,7 +221,7 @@ int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
va_list args_copy; va_list args_copy;
const char *bufiter = buf; const char *bufiter = buf;
int ret = 0; int ret = 0;
int64_t startpos = 0; int64_t startpos = filestream_tell(stream);
int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1); int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1);
if (maxlen <= 0) if (maxlen <= 0)
@ -330,7 +330,6 @@ int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
} }
va_end(args_copy); va_end(args_copy);
startpos = filestream_tell(stream);
filestream_seek(stream, startpos + (bufiter - buf), filestream_seek(stream, startpos + (bufiter - buf),
RETRO_VFS_SEEK_POSITION_START); RETRO_VFS_SEEK_POSITION_START);

View File

@ -217,27 +217,24 @@ char *string_trim_whitespace(char *const s)
* correctly any text containing so-called 'wide' Unicode * correctly any text containing so-called 'wide' Unicode
* characters (e.g. CJK languages, emojis, etc.). * characters (e.g. CJK languages, emojis, etc.).
**/ **/
void word_wrap( size_t word_wrap(
char *dst, size_t dst_size, char *dst, size_t dst_size,
const char *src, size_t src_len, const char *src, size_t src_len,
int line_width, int wideglyph_width, unsigned max_lines) int line_width, int wideglyph_width, unsigned max_lines)
{ {
char *lastspace = NULL; char *last_space = NULL;
unsigned counter = 0; unsigned counter = 0;
unsigned lines = 1; unsigned lines = 1;
const char *src_end = src + src_len; const char *src_end = src + src_len;
/* Prevent buffer overflow */ /* Prevent buffer overflow */
if (dst_size < src_len + 1) if (dst_size < src_len + 1)
return; return 0;
/* Early return if src string length is less /* Early return if src string length is less
* than line width */ * than line width */
if (src_len < (size_t)line_width) if (src_len < (size_t)line_width)
{ return strlcpy(dst, src, dst_size);
strlcpy(dst, src, dst_size);
return;
}
while (*src != '\0') while (*src != '\0')
{ {
@ -245,21 +242,18 @@ void word_wrap(
counter++; counter++;
if (*src == ' ') if (*src == ' ')
lastspace = dst; /* Remember the location of the whitespace */ last_space = dst; /* Remember the location of the whitespace */
else if (*src == '\n') else if (*src == '\n')
{ {
/* If newlines embedded in the input, /* If newlines embedded in the input,
* reset the index */ * reset the index */
lines++; lines++;
counter = 0; counter = 0;
/* Early return if remaining src string /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src <= line_width) if (src_end - src <= line_width)
{ return strlcpy(dst, src, dst_size);
strlcpy(dst, src, dst_size);
return;
}
} }
while (char_len--) while (char_len--)
@ -269,29 +263,27 @@ void word_wrap(
{ {
counter = 0; counter = 0;
if (lastspace && (max_lines == 0 || lines < max_lines)) if (last_space && (max_lines == 0 || lines < max_lines))
{ {
/* Replace nearest (previous) whitespace /* Replace nearest (previous) whitespace
* with newline character */ * with newline character */
*lastspace = '\n'; *last_space = '\n';
lines++; lines++;
src -= dst - lastspace - 1; src -= dst - last_space - 1;
dst = lastspace + 1; dst = last_space + 1;
lastspace = NULL; last_space = NULL;
/* Early return if remaining src string /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src < line_width) if (src_end - src < line_width)
{ return strlcpy(dst, src, dst_size);
strlcpy(dst, src, dst_size);
return;
}
} }
} }
} }
*dst = '\0'; *dst = '\0';
return 0;
} }
/** /**
@ -327,7 +319,7 @@ void word_wrap(
* on-screen pixel width deviates greatly from the set * on-screen pixel width deviates greatly from the set
* @wideglyph_width value. * @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, const char *src, size_t src_len, int line_width,
int wideglyph_width, unsigned max_lines) int wideglyph_width, unsigned max_lines)
{ {
@ -359,14 +351,11 @@ void word_wrap_wideglyph(char *dst, size_t dst_size,
unsigned counter_normalized = 0; unsigned counter_normalized = 0;
int line_width_normalized = line_width * 100; int line_width_normalized = line_width * 100;
int additional_counter_normalized = wideglyph_width - 100; int additional_counter_normalized = wideglyph_width - 100;
/* Early return if src string length is less /* Early return if src string length is less
* than line width */ * than line width */
if (src_end - src < line_width) if (src_end - src < line_width)
{ return strlcpy(dst, src, dst_size);
strlcpy(dst, src, dst_size);
return;
}
while (*src != '\0') while (*src != '\0')
{ {
@ -389,10 +378,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size,
/* Early return if remaining src string /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src <= line_width) if (src_end - src <= line_width)
{ return strlcpy(dst, src, dst_size);
strlcpy(dst, src, dst_size);
return;
}
} }
else if (char_len >= 3) 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 /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src <= line_width) if (src_end - src <= line_width)
{ return strlcpy(dst, src, dst_size);
strlcpy(dst, src, dst_size);
return;
}
} }
else if (lastspace) else if (lastspace)
{ {
@ -442,15 +425,13 @@ void word_wrap_wideglyph(char *dst, size_t dst_size,
/* Early return if remaining src string /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src < line_width) if (src_end - src < line_width)
{ return strlcpy(dst, src, dst_size);
strlcpy(dst, src, dst_size);
return;
}
} }
} }
} }
*dst = '\0'; *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] != '\0')
{ {
if ( (str[0] == '0') && if ( (str[0] == '0') &&
((str[1] == 'x') || ((str[1] == 'x') ||
(str[1] == 'X'))) (str[1] == 'X')))
{ {
hex_str = str + 2; 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: * string_replace_whitespace_with_single_character:
* *
* Leaf function. * Leaf function.
* *
* Replaces all spaces with given character @c. * Replaces all spaces with given character @c.

View File

@ -301,7 +301,7 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
{ {
int flags = 0; int flags = 0;
const char *mode_str = NULL; const char *mode_str = NULL;
libretro_vfs_implementation_file *stream = libretro_vfs_implementation_file *stream =
(libretro_vfs_implementation_file*) (libretro_vfs_implementation_file*)
malloc(sizeof(*stream)); 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 * 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 * a buffer of the given size will be allocated immediately, and
* released on close. This is an extension to ANSI C. * 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. * 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 */ * find which and add ifdef */
#if defined(_3DS) #if defined(_3DS)
if (stream->scheme != VFS_SCHEME_CDROM) 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 #ifdef HAVE_MMAP
/* Need to check stream->mapped because this function /* Need to check stream->mapped because this function
* is called in filestream_open() */ * is called in filestream_open() */
if (stream->mapped && stream->hints & if (stream->mapped && stream->hints &
RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
return stream->mappos; return stream->mappos;
#endif #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 retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len)
{ {
int64_t pos = 0; int64_t pos = 0;
size_t result = -1; ssize_t result = -1;
if (!stream) if (!stream)
return -1; 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) int retro_vfs_file_remove_impl(const char *path)
{ {
if (path && *path)
{
int ret = -1;
#if defined(_WIN32) && !defined(_XBOX) #if defined(_WIN32) && !defined(_XBOX)
/* Win32 (no Xbox) */ /* Win32 (no Xbox) */
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 #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 #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 #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) if (ret == 0)
return 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; 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) int retro_vfs_stat_impl(const char *path, int32_t *size)
{ {
bool is_dir = false; int ret = RETRO_VFS_STAT_IS_VALID;
bool is_character_special = false;
if (!path || !*path)
return 0;
{
#if defined(VITA) #if defined(VITA)
/* Vita / PSP */ /* Vita / PSP */
SceIoStat buf; SceIoStat stat_buf;
int dir_ret; int dir_ret;
char *tmp = NULL; char *tmp = strdup(path);
size_t len = 0; size_t len = strlen(tmp);
if (tmp[len-1] == '/')
tmp[len-1] = '\0';
if (!path || !*path) dir_ret = sceIoGetstat(tmp, &stat_buf);
return 0; free(tmp);
if (dir_ret < 0)
return 0;
tmp = strdup(path); if (size)
len = strlen(tmp); *size = (int32_t)stat_buf.st_size;
if (tmp[len-1] == '/')
tmp[len-1] = '\0';
dir_ret = sceIoGetstat(tmp, &buf); if (FIO_S_ISDIR(stat_buf.st_mode))
free(tmp); ret |= RETRO_VFS_STAT_IS_DIRECTORY;
if (dir_ret < 0)
return 0;
if (size)
*size = (int32_t)buf.st_size;
is_dir = FIO_S_ISDIR(buf.st_mode);
#elif defined(__PSL1GHT__) || defined(__PS3__) #elif defined(__PSL1GHT__) || defined(__PS3__)
/* Lowlevel Lv2 */ /* Lowlevel Lv2 */
sysFSStat buf; sysFSStat stat_buf;
if (!path || !*path) if (sysFsStat(path, &stat_buf) < 0)
return 0; return 0;
if (sysFsStat(path, &buf) < 0)
return 0;
if (size) if (size)
*size = (int32_t)buf.st_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) #elif defined(_WIN32)
/* Windows */ /* Windows */
DWORD file_info; struct _stat stat_buf;
struct _stat buf;
#if defined(LEGACY_WIN32) #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 #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 #endif
if (file_info == INVALID_FILE_ATTRIBUTES)
return 0;
if (!path || !*path) if (size)
return 0; *size = (int32_t)stat_buf.st_size;
#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 (file_info & FILE_ATTRIBUTE_DIRECTORY)
ret |= RETRO_VFS_STAT_IS_DIRECTORY;
#elif defined(GEKKO) #elif defined(GEKKO)
/* On GEKKO platforms, paths cannot have /* On GEKKO platforms, paths cannot have
* trailing slashes - we must therefore * trailing slashes - we must therefore
* remove them */ * remove them */
char *path_buf = NULL; size_t len;
int stat_ret = -1; char *path_buf = NULL;
struct stat stat_buf; struct stat stat_buf;
size_t len;
if (string_is_empty(path)) if (!(path_buf = strdup(path)))
return 0; return 0;
if (!(path_buf = strdup(path))) if ((len = strlen(path_buf)) > 0)
return 0; if (path_buf[len - 1] == '/')
path_buf[len - 1] = '\0';
if ((len = strlen(path_buf)) > 0) if (stat(path_buf, &stat_buf) < 0)
if (path_buf[len - 1] == '/') {
path_buf[len - 1] = '\0'; free(path_buf);
return 0;
}
stat_ret = stat(path_buf, &stat_buf); free(path_buf);
free(path_buf);
if (size)
if (stat_ret < 0) *size = (int32_t)stat_buf.st_size;
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);
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 #else
/* Every other platform */ /* Every other platform */
struct stat buf; struct stat stat_buf;
if (!path || !*path) if (stat(path, &stat_buf) < 0)
return 0; return 0;
if (stat(path, &buf) < 0)
return 0;
if (size) if (size)
*size = (int32_t)buf.st_size; *size = (int32_t)stat_buf.st_size;
is_dir = S_ISDIR(buf.st_mode); if (S_ISDIR(stat_buf.st_mode))
is_character_special = S_ISCHR(buf.st_mode); ret |= RETRO_VFS_STAT_IS_DIRECTORY;
if (S_ISCHR(stat_buf.st_mode))
ret |= RETRO_VFS_STAT_IS_CHARACTER_SPECIAL;
#endif #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) #if defined(VITA)