mirror of
https://github.com/libretro/vba-next.git
synced 2024-11-27 01:40:21 +00:00
Merge pull request #150 from Aftnet/master
Updated vfs.h to match current libretro common
This commit is contained in:
commit
2c726f25da
@ -60,10 +60,3 @@ size_t strlcat(char *dest, const char *source, size_t size)
|
||||
return len + strlcpy(dest, source, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
char *strldup(const char *s, size_t n)
|
||||
{
|
||||
char *dst = (char*)malloc(sizeof(char) * (n + 1));
|
||||
strlcpy(dst, s, n);
|
||||
return dst;
|
||||
}
|
||||
|
@ -37,27 +37,28 @@
|
||||
void *fopen_utf8(const char * filename, const char * mode)
|
||||
{
|
||||
#if defined(LEGACY_WIN32)
|
||||
FILE *ret = NULL;
|
||||
char * filename_local = utf8_to_local_string_alloc(filename);
|
||||
|
||||
if (!filename_local)
|
||||
return NULL;
|
||||
ret = fopen(filename_local, mode);
|
||||
if (filename_local)
|
||||
{
|
||||
FILE *ret = fopen(filename_local, mode);
|
||||
free(filename_local);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
|
||||
wchar_t * mode_w = utf8_to_utf16_string_alloc(mode);
|
||||
FILE* ret = NULL;
|
||||
|
||||
if (filename_w && mode_w)
|
||||
ret = _wfopen(filename_w, mode_w);
|
||||
if (filename_w)
|
||||
free(filename_w);
|
||||
{
|
||||
FILE *ret = NULL;
|
||||
wchar_t *mode_w = utf8_to_utf16_string_alloc(mode);
|
||||
if (mode_w)
|
||||
{
|
||||
ret = _wfopen(filename_w, mode_w);
|
||||
free(mode_w);
|
||||
}
|
||||
free(filename_w);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
@ -51,9 +51,12 @@ static unsigned leading_ones(uint8_t c)
|
||||
return ones;
|
||||
}
|
||||
|
||||
/* Simple implementation. Assumes the sequence is
|
||||
* properly synchronized and terminated. */
|
||||
|
||||
/**
|
||||
* utf8_conv_utf32:
|
||||
*
|
||||
* Simple implementation. Assumes the sequence is
|
||||
* properly synchronized and terminated.
|
||||
**/
|
||||
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
|
||||
const char *in, size_t in_size)
|
||||
{
|
||||
@ -88,6 +91,11 @@ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf16_conv_utf8:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
||||
const uint16_t *in, size_t in_size)
|
||||
{
|
||||
@ -148,16 +156,20 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Acts mostly like strlcpy.
|
||||
/**
|
||||
* utf8cpy:
|
||||
*
|
||||
* Acts mostly like strlcpy.
|
||||
*
|
||||
* Copies the given number of UTF-8 characters,
|
||||
* but at most d_len bytes.
|
||||
* but at most @d_len bytes.
|
||||
*
|
||||
* Always NULL terminates.
|
||||
* Does not copy half a character.
|
||||
* Always NULL terminates. Does not copy half a character.
|
||||
* @s is assumed valid UTF-8.
|
||||
* Use only if @chars is considerably less than @d_len.
|
||||
*
|
||||
* Returns number of bytes. 's' is assumed valid UTF-8.
|
||||
* Use only if 'chars' is considerably less than 'd_len'. */
|
||||
* @return Number of bytes.
|
||||
**/
|
||||
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
|
||||
{
|
||||
const uint8_t *sb = (const uint8_t*)s;
|
||||
@ -186,6 +198,11 @@ size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
|
||||
return sb-sb_org;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8skip:
|
||||
*
|
||||
* Leaf function
|
||||
**/
|
||||
const char *utf8skip(const char *str, size_t chars)
|
||||
{
|
||||
const uint8_t *strb = (const uint8_t*)str;
|
||||
@ -204,6 +221,11 @@ const char *utf8skip(const char *str, size_t chars)
|
||||
return (const char*)strb;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8len:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
size_t utf8len(const char *string)
|
||||
{
|
||||
size_t ret = 0;
|
||||
@ -220,7 +242,15 @@ size_t utf8len(const char *string)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Does not validate the input, returns garbage if it's not UTF-8. */
|
||||
/**
|
||||
* utf8_walk:
|
||||
*
|
||||
* Does not validate the input.
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return Returns garbage if it's not UTF-8.
|
||||
**/
|
||||
uint32_t utf8_walk(const char **string)
|
||||
{
|
||||
uint8_t first = UTF8_WALKBYTE(string);
|
||||
@ -248,19 +278,18 @@ static bool utf16_to_char(uint8_t **utf_data,
|
||||
size_t *dest_len, const uint16_t *in)
|
||||
{
|
||||
unsigned len = 0;
|
||||
|
||||
while (in[len] != '\0')
|
||||
len++;
|
||||
|
||||
utf16_conv_utf8(NULL, dest_len, in, len);
|
||||
*dest_len += 1;
|
||||
*utf_data = (uint8_t*)malloc(*dest_len);
|
||||
if (*utf_data == 0)
|
||||
return false;
|
||||
|
||||
if ((*utf_data = (uint8_t*)malloc(*dest_len)) != 0)
|
||||
return utf16_conv_utf8(*utf_data, dest_len, in, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf16_to_char_string:
|
||||
**/
|
||||
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
|
||||
{
|
||||
size_t dest_len = 0;
|
||||
@ -280,7 +309,11 @@ bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
/**
|
||||
* mb_to_mb_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
static char *mb_to_mb_string_alloc(const char *str,
|
||||
enum CodePage cp_in, enum CodePage cp_out)
|
||||
{
|
||||
@ -300,10 +333,8 @@ static char *mb_to_mb_string_alloc(const char *str,
|
||||
if (!path_buf_wide_len)
|
||||
return strdup(str);
|
||||
|
||||
path_buf_wide = (wchar_t*)
|
||||
calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t));
|
||||
|
||||
if (path_buf_wide)
|
||||
if ((path_buf_wide = (wchar_t*)
|
||||
calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t))))
|
||||
{
|
||||
MultiByteToWideChar(cp_in, 0,
|
||||
str, -1, path_buf_wide, path_buf_wide_len);
|
||||
@ -347,45 +378,49 @@ static char *mb_to_mb_string_alloc(const char *str,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
/**
|
||||
* utf8_to_local_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char* utf8_to_local_string_alloc(const char *str)
|
||||
{
|
||||
if (str && *str)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
|
||||
return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL);
|
||||
#else
|
||||
/* assume string needs no modification if not on Windows */
|
||||
return strdup(str);
|
||||
return strdup(str); /* Assume string needs no modification if not on Windows */
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
char* local_to_utf8_string_alloc(const char *str)
|
||||
/**
|
||||
* local_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *local_to_utf8_string_alloc(const char *str)
|
||||
{
|
||||
if (str && *str)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
|
||||
return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
|
||||
#else
|
||||
/* assume string needs no modification if not on Windows */
|
||||
return strdup(str);
|
||||
return strdup(str); /* Assume string needs no modification if not on Windows */
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
/**
|
||||
* utf8_to_utf16_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
wchar_t* utf8_to_utf16_string_alloc(const char *str)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int len = 0;
|
||||
int out_len = 0;
|
||||
#else
|
||||
size_t len = 0;
|
||||
size_t out_len = 0;
|
||||
#endif
|
||||
wchar_t *buf = NULL;
|
||||
|
||||
@ -393,63 +428,55 @@ wchar_t* utf8_to_utf16_string_alloc(const char *str)
|
||||
return NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
||||
|
||||
if (len)
|
||||
if ((len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)))
|
||||
{
|
||||
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
|
||||
return NULL;
|
||||
|
||||
out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len);
|
||||
if ((MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len)) < 0)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fallback to ANSI codepage instead */
|
||||
len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
|
||||
|
||||
if (len)
|
||||
/* Fallback to ANSI codepage instead */
|
||||
if ((len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0)))
|
||||
{
|
||||
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
|
||||
return NULL;
|
||||
|
||||
out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
if (out_len < 0)
|
||||
if ((MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len)) < 0)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
|
||||
len = mbstowcs(NULL, str, 0) + 1;
|
||||
|
||||
if (len)
|
||||
if ((len = mbstowcs(NULL, str, 0) + 1))
|
||||
{
|
||||
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
|
||||
return NULL;
|
||||
|
||||
out_len = mbstowcs(buf, str, len);
|
||||
}
|
||||
|
||||
if (out_len == (size_t)-1)
|
||||
if ((mbstowcs(buf, str, len)) == (size_t)-1)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
/**
|
||||
* utf16_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char* utf16_to_utf8_string_alloc(const wchar_t *str)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@ -465,20 +492,17 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str)
|
||||
#ifdef _WIN32
|
||||
{
|
||||
UINT code_page = CP_UTF8;
|
||||
len = WideCharToMultiByte(code_page,
|
||||
0, str, -1, NULL, 0, NULL, NULL);
|
||||
|
||||
/* fallback to ANSI codepage instead */
|
||||
if (!len)
|
||||
if (!(len = WideCharToMultiByte(code_page,
|
||||
0, str, -1, NULL, 0, NULL, NULL)))
|
||||
{
|
||||
code_page = CP_ACP;
|
||||
len = WideCharToMultiByte(code_page,
|
||||
0, str, -1, NULL, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
buf = (char*)calloc(len, sizeof(char));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (char*)calloc(len, sizeof(char))))
|
||||
return NULL;
|
||||
|
||||
if (WideCharToMultiByte(code_page,
|
||||
@ -491,13 +515,9 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str)
|
||||
#else
|
||||
/* NOTE: For now, assume non-Windows platforms'
|
||||
* locale is already UTF-8. */
|
||||
len = wcstombs(NULL, str, 0) + 1;
|
||||
|
||||
if (len)
|
||||
if ((len = wcstombs(NULL, str, 0) + 1))
|
||||
{
|
||||
buf = (char*)calloc(len, sizeof(char));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (char*)calloc(len, sizeof(char))))
|
||||
return NULL;
|
||||
|
||||
if (wcstombs(buf, str, len) == (size_t)-1)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -24,13 +24,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <file/file_path.h>
|
||||
#include <retro_assert.h>
|
||||
#include <compat/strl.h>
|
||||
#include <compat/posix_string.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
@ -74,7 +72,7 @@ int path_stat(const char *path)
|
||||
*
|
||||
* Checks if path is a directory.
|
||||
*
|
||||
* Returns: true (1) if path is a directory, otherwise false (0).
|
||||
* @return true if path is a directory, otherwise false.
|
||||
*/
|
||||
bool path_is_directory(const char *path)
|
||||
{
|
||||
@ -106,7 +104,9 @@ int32_t path_get_size(const char *path)
|
||||
*
|
||||
* Create directory on filesystem.
|
||||
*
|
||||
* Returns: true (1) if directory could be created, otherwise false (0).
|
||||
* Recursive function.
|
||||
*
|
||||
* @return true if directory could be created, otherwise false.
|
||||
**/
|
||||
bool path_mkdir(const char *dir)
|
||||
{
|
||||
@ -118,12 +118,10 @@ bool path_mkdir(const char *dir)
|
||||
|
||||
/* Use heap. Real chance of stack
|
||||
* overflow if we recurse too hard. */
|
||||
basedir = strdup(dir);
|
||||
|
||||
if (!basedir)
|
||||
if (!(basedir = strdup(dir)))
|
||||
return false;
|
||||
|
||||
path_parent_dir(basedir);
|
||||
path_parent_dir(basedir, strlen(basedir));
|
||||
|
||||
if (!*basedir || !strcmp(basedir, dir))
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (apple_compat.h).
|
||||
@ -20,6 +20,9 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __APPLE_COMPAT_H
|
||||
#define __APPLE_COMPAT_H
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
@ -32,6 +35,32 @@ typedef unsigned NSUInteger;
|
||||
typedef float CGFloat;
|
||||
#endif
|
||||
|
||||
#ifndef __has_feature
|
||||
/* Compatibility with non-Clang compilers. */
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef CF_RETURNS_RETAINED
|
||||
#if __has_feature(attribute_cf_returns_retained)
|
||||
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
|
||||
#else
|
||||
#define CF_RETURNS_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NS_INLINE
|
||||
#define NS_INLINE inline
|
||||
#endif
|
||||
|
||||
NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetainCompat(id X)
|
||||
{
|
||||
#if __has_feature(objc_arc)
|
||||
return (__bridge_retained CFTypeRef)X;
|
||||
#else
|
||||
return X;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef IOS
|
||||
@ -43,7 +72,13 @@ typedef float CGFloat;
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <GLKit/GLKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef __OBJC__
|
||||
#include <objc/objc-runtime.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (compat_fnmatch.h).
|
||||
* The following license statement only applies to this file (fnmatch.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (compat_getopt.h).
|
||||
* The following license statement only applies to this file (getopt.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
@ -72,4 +72,3 @@ RETRO_END_DECLS
|
||||
|
||||
/* pragma once */
|
||||
#endif
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (intrinsics.h).
|
||||
@ -30,16 +30,18 @@
|
||||
#include <retro_common_api.h>
|
||||
#include <retro_inline.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER) && !defined(_XBOX)
|
||||
#if (_MSC_VER > 1310)
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/* Count Leading Zero, unsigned 16bit input value */
|
||||
static INLINE unsigned compat_clz_u16(uint16_t val)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
#if defined(__GNUC__)
|
||||
return __builtin_clz(val << 16 | 0x8000);
|
||||
#else
|
||||
unsigned ret = 0;
|
||||
@ -55,34 +57,42 @@ static INLINE unsigned compat_clz_u16(uint16_t val)
|
||||
}
|
||||
|
||||
/* Count Trailing Zero */
|
||||
static INLINE int compat_ctz(unsigned x)
|
||||
{
|
||||
#if defined(__GNUC__) && !defined(RARCH_CONSOLE)
|
||||
static INLINE int compat_ctz(unsigned x)
|
||||
{
|
||||
return __builtin_ctz(x);
|
||||
}
|
||||
#elif _MSC_VER >= 1400
|
||||
static INLINE int compat_ctz(unsigned x)
|
||||
{
|
||||
#elif _MSC_VER >= 1400 && !defined(_XBOX) && !defined(__WINRT__)
|
||||
unsigned long r = 0;
|
||||
_BitScanReverse((unsigned long*)&r, x);
|
||||
_BitScanForward((unsigned long*)&r, x);
|
||||
return (int)r;
|
||||
}
|
||||
#else
|
||||
/* Only checks at nibble granularity,
|
||||
* because that's what we need. */
|
||||
static INLINE int compat_ctz(unsigned x)
|
||||
{
|
||||
if (x & 0x000f)
|
||||
return 0;
|
||||
if (x & 0x00f0)
|
||||
return 4;
|
||||
if (x & 0x0f00)
|
||||
return 8;
|
||||
if (x & 0xf000)
|
||||
return 12;
|
||||
return 16;
|
||||
}
|
||||
int count = 0;
|
||||
if (!(x & 0xffff))
|
||||
{
|
||||
x >>= 16;
|
||||
count |= 16;
|
||||
}
|
||||
if (!(x & 0xff))
|
||||
{
|
||||
x >>= 8;
|
||||
count |= 8;
|
||||
}
|
||||
if (!(x & 0xf))
|
||||
{
|
||||
x >>= 4;
|
||||
count |= 4;
|
||||
}
|
||||
if (!(x & 0x3))
|
||||
{
|
||||
x >>= 2;
|
||||
count |= 2;
|
||||
}
|
||||
if (!(x & 0x1))
|
||||
count |= 1;
|
||||
|
||||
return count;
|
||||
#endif
|
||||
}
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#ifdef WANT_ZLIB
|
||||
|
||||
#ifdef RARCH_INTERNAL
|
||||
#include "../../../deps/zlib/zconf.h.in"
|
||||
#include "zconf.h.in"
|
||||
#endif
|
||||
|
||||
/* zlib.h -- interface of the 'zlib' general purpose compression library
|
||||
@ -31,7 +31,6 @@
|
||||
Jean-loup Gailly Mark Adler
|
||||
jloup@gzip.org madler@alumni.caltech.edu
|
||||
|
||||
|
||||
The data format used by the zlib library is described by RFCs (Request for
|
||||
Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
|
||||
(zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
|
||||
@ -40,6 +39,7 @@
|
||||
#ifndef ZLIB_H
|
||||
#define ZLIB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "zconf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -86,8 +86,8 @@ extern "C" {
|
||||
even in case of corrupted input.
|
||||
*/
|
||||
|
||||
typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
|
||||
typedef void (*free_func) OF((voidpf opaque, voidpf address));
|
||||
typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size);
|
||||
typedef void (*free_func) (voidpf opaque, voidpf address);
|
||||
|
||||
struct internal_state;
|
||||
|
||||
@ -219,10 +219,9 @@ typedef gz_header FAR *gz_headerp;
|
||||
#define zlib_version zlibVersion()
|
||||
/* for compatibility with versions < 1.0.2 */
|
||||
|
||||
|
||||
/* basic functions */
|
||||
|
||||
ZEXTERN const char * ZEXPORT zlibVersion OF((void));
|
||||
const char * zlibVersion (void);
|
||||
/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
|
||||
If the first character differs, the library code actually used is not
|
||||
compatible with the zlib.h header file used by the application. This check
|
||||
@ -230,7 +229,7 @@ ZEXTERN const char * ZEXPORT zlibVersion OF((void));
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
|
||||
int deflateInit (z_streamp strm, int level);
|
||||
|
||||
Initializes the internal stream state for compression. The fields
|
||||
zalloc, zfree and opaque must be initialized before by the caller. If
|
||||
@ -251,8 +250,7 @@ ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
|
||||
this will be done by deflate().
|
||||
*/
|
||||
|
||||
|
||||
ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
|
||||
int deflate (z_streamp strm, int flush);
|
||||
/*
|
||||
deflate compresses as much data as possible, and stops when the input
|
||||
buffer becomes empty or the output buffer becomes full. It may introduce
|
||||
@ -358,8 +356,7 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
|
||||
space to continue compressing.
|
||||
*/
|
||||
|
||||
|
||||
ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
|
||||
int deflateEnd (z_streamp strm);
|
||||
/*
|
||||
All dynamically allocated data structures for this stream are freed.
|
||||
This function discards any unprocessed input and does not flush any pending
|
||||
@ -372,9 +369,8 @@ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
|
||||
deallocated).
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
|
||||
int inflateInit (z_streamp strm);
|
||||
|
||||
Initializes the internal stream state for decompression. The fields
|
||||
next_in, avail_in, zalloc, zfree and opaque must be initialized before by
|
||||
@ -397,8 +393,7 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
|
||||
until inflate() is called.
|
||||
*/
|
||||
|
||||
|
||||
ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
|
||||
int inflate (z_streamp strm, int flush);
|
||||
/*
|
||||
inflate decompresses as much data as possible, and stops when the input
|
||||
buffer becomes empty or the output buffer becomes full. It may introduce
|
||||
@ -513,8 +508,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
|
||||
recovery of the data is desired.
|
||||
*/
|
||||
|
||||
|
||||
ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
|
||||
int inflateEnd (z_streamp strm);
|
||||
/*
|
||||
All dynamically allocated data structures for this stream are freed.
|
||||
This function discards any unprocessed input and does not flush any pending
|
||||
@ -525,7 +519,6 @@ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
|
||||
static string (which must not be deallocated).
|
||||
*/
|
||||
|
||||
|
||||
/* Advanced functions */
|
||||
|
||||
/*
|
||||
@ -533,12 +526,12 @@ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
|
||||
int deflateInit2 (z_streamp strm,
|
||||
int level,
|
||||
int method,
|
||||
int windowBits,
|
||||
int memLevel,
|
||||
int strategy));
|
||||
int strategy);
|
||||
|
||||
This is another version of deflateInit with more compression options. The
|
||||
fields next_in, zalloc, zfree and opaque must be initialized before by the
|
||||
@ -593,9 +586,9 @@ ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
|
||||
compression: this will be done by deflate().
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
|
||||
int deflateSetDictionary (z_streamp strm,
|
||||
const Bytef *dictionary,
|
||||
uInt dictLength));
|
||||
uInt dictLength);
|
||||
/*
|
||||
Initializes the compression dictionary from the given byte sequence
|
||||
without producing any compressed output. When using the zlib format, this
|
||||
@ -637,8 +630,8 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
|
||||
not perform any compression: this will be done by deflate().
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
|
||||
z_streamp source));
|
||||
int deflateCopy (z_streamp dest,
|
||||
z_streamp source);
|
||||
/*
|
||||
Sets the destination stream as a complete copy of the source stream.
|
||||
|
||||
@ -655,7 +648,7 @@ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
|
||||
destination.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
|
||||
int deflateReset (z_streamp strm);
|
||||
/*
|
||||
This function is equivalent to deflateEnd followed by deflateInit,
|
||||
but does not free and reallocate all the internal compression state. The
|
||||
@ -666,9 +659,9 @@ ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
|
||||
stream state was inconsistent (such as zalloc or state being Z_NULL).
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
|
||||
int deflateParams (z_streamp strm,
|
||||
int level,
|
||||
int strategy));
|
||||
int strategy);
|
||||
/*
|
||||
Dynamically update the compression level and compression strategy. The
|
||||
interpretation of level and strategy is as in deflateInit2. This can be
|
||||
@ -687,11 +680,11 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
|
||||
strm->avail_out was zero.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
|
||||
int deflateTune (z_streamp strm,
|
||||
int good_length,
|
||||
int max_lazy,
|
||||
int nice_length,
|
||||
int max_chain));
|
||||
int max_chain);
|
||||
/*
|
||||
Fine tune deflate's internal compression parameters. This should only be
|
||||
used by someone who understands the algorithm used by zlib's deflate for
|
||||
@ -704,8 +697,8 @@ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
|
||||
returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
|
||||
*/
|
||||
|
||||
ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
|
||||
uLong sourceLen));
|
||||
uLong deflateBound (z_streamp strm,
|
||||
uLong sourceLen);
|
||||
/*
|
||||
deflateBound() returns an upper bound on the compressed size after
|
||||
deflation of sourceLen bytes. It must be called after deflateInit() or
|
||||
@ -719,9 +712,9 @@ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
|
||||
than Z_FINISH or Z_NO_FLUSH are used.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
|
||||
int deflatePending (z_streamp strm,
|
||||
unsigned *pending,
|
||||
int *bits));
|
||||
int *bits);
|
||||
/*
|
||||
deflatePending() returns the number of bytes and bits of output that have
|
||||
been generated, but not yet provided in the available output. The bytes not
|
||||
@ -734,9 +727,9 @@ ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
|
||||
stream state was inconsistent.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
|
||||
int deflatePrime (z_streamp strm,
|
||||
int bits,
|
||||
int value));
|
||||
int value);
|
||||
/*
|
||||
deflatePrime() inserts bits in the deflate output stream. The intent
|
||||
is that this function is used to start off the deflate output with the bits
|
||||
@ -751,8 +744,8 @@ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
|
||||
source stream state was inconsistent.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
|
||||
gz_headerp head));
|
||||
int deflateSetHeader (z_streamp strm,
|
||||
gz_headerp head);
|
||||
/*
|
||||
deflateSetHeader() provides gzip header information for when a gzip
|
||||
stream is requested by deflateInit2(). deflateSetHeader() may be called
|
||||
@ -776,8 +769,8 @@ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
|
||||
int windowBits));
|
||||
int inflateInit2 (z_streamp strm,
|
||||
int windowBits);
|
||||
|
||||
This is another version of inflateInit with an extra parameter. The
|
||||
fields next_in, avail_in, zalloc, zfree and opaque must be initialized
|
||||
@ -825,9 +818,9 @@ ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
|
||||
deferred until inflate() is called.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
|
||||
int inflateSetDictionary (z_streamp strm,
|
||||
const Bytef *dictionary,
|
||||
uInt dictLength));
|
||||
uInt dictLength);
|
||||
/*
|
||||
Initializes the decompression dictionary from the given uncompressed byte
|
||||
sequence. This function must be called immediately after a call of inflate,
|
||||
@ -848,9 +841,9 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
|
||||
inflate().
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm,
|
||||
int inflateGetDictionary (z_streamp strm,
|
||||
Bytef *dictionary,
|
||||
uInt *dictLength));
|
||||
uInt *dictLength);
|
||||
/*
|
||||
Returns the sliding dictionary being maintained by inflate. dictLength is
|
||||
set to the number of bytes in the dictionary, and that many bytes are copied
|
||||
@ -863,7 +856,7 @@ ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm,
|
||||
stream state is inconsistent.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
|
||||
int inflateSync (z_streamp strm);
|
||||
/*
|
||||
Skips invalid compressed data until a possible full flush point (see above
|
||||
for the description of deflate with Z_FULL_FLUSH) can be found, or until all
|
||||
@ -882,8 +875,8 @@ ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
|
||||
input each time, until success or end of the input data.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
|
||||
z_streamp source));
|
||||
int inflateCopy (z_streamp dest,
|
||||
z_streamp source);
|
||||
/*
|
||||
Sets the destination stream as a complete copy of the source stream.
|
||||
|
||||
@ -898,7 +891,7 @@ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
|
||||
destination.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
|
||||
int inflateReset (z_streamp strm);
|
||||
/*
|
||||
This function is equivalent to inflateEnd followed by inflateInit,
|
||||
but does not free and reallocate all the internal decompression state. The
|
||||
@ -908,8 +901,8 @@ ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
|
||||
stream state was inconsistent (such as zalloc or state being Z_NULL).
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
|
||||
int windowBits));
|
||||
int inflateReset2 (z_streamp strm,
|
||||
int windowBits);
|
||||
/*
|
||||
This function is the same as inflateReset, but it also permits changing
|
||||
the wrap and window size requests. The windowBits parameter is interpreted
|
||||
@ -920,9 +913,9 @@ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
|
||||
the windowBits parameter is invalid.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
|
||||
int inflatePrime (z_streamp strm,
|
||||
int bits,
|
||||
int value));
|
||||
int value);
|
||||
/*
|
||||
This function inserts bits in the inflate input stream. The intent is
|
||||
that this function is used to start inflating at a bit position in the
|
||||
@ -941,7 +934,7 @@ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
|
||||
stream state was inconsistent.
|
||||
*/
|
||||
|
||||
ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
|
||||
long inflateMark (z_streamp strm);
|
||||
/*
|
||||
This function returns two values, one in the lower 16 bits of the return
|
||||
value, and the other in the remaining upper bits, obtained by shifting the
|
||||
@ -969,8 +962,8 @@ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
|
||||
source stream state was inconsistent.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
|
||||
gz_headerp head));
|
||||
int inflateGetHeader (z_streamp strm,
|
||||
gz_headerp head);
|
||||
/*
|
||||
inflateGetHeader() requests that gzip header information be stored in the
|
||||
provided gz_header structure. inflateGetHeader() may be called after
|
||||
@ -1010,8 +1003,8 @@ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
|
||||
unsigned char FAR *window));
|
||||
int inflateBackInit (z_streamp strm, int windowBits,
|
||||
unsigned char FAR *window);
|
||||
|
||||
Initialize the internal stream state for decompression using inflateBack()
|
||||
calls. The fields zalloc, zfree and opaque in strm must be initialized
|
||||
@ -1031,13 +1024,13 @@ ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
|
||||
the version of the header file.
|
||||
*/
|
||||
|
||||
typedef unsigned (*in_func) OF((void FAR *,
|
||||
z_const unsigned char FAR * FAR *));
|
||||
typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
|
||||
typedef unsigned (*in_func) (void FAR *,
|
||||
z_const unsigned char FAR * FAR *);
|
||||
typedef int (*out_func) (void FAR *, unsigned char FAR *, unsigned);
|
||||
|
||||
ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
|
||||
int inflateBack (z_streamp strm,
|
||||
in_func in, void FAR *in_desc,
|
||||
out_func out, void FAR *out_desc));
|
||||
out_func out, void FAR *out_desc);
|
||||
/*
|
||||
inflateBack() does a raw inflate with a single call using a call-back
|
||||
interface for input and output. This is potentially more efficient than
|
||||
@ -1105,7 +1098,7 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
|
||||
cannot return Z_OK.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
|
||||
int inflateBackEnd (z_streamp strm);
|
||||
/*
|
||||
All memory allocated by inflateBackInit() is freed.
|
||||
|
||||
@ -1113,7 +1106,7 @@ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
|
||||
state was inconsistent.
|
||||
*/
|
||||
|
||||
ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
|
||||
uLong zlibCompileFlags (void);
|
||||
/* Return flags indicating compile-time options.
|
||||
|
||||
Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
|
||||
@ -1166,8 +1159,8 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
|
||||
you need special options.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
|
||||
const Bytef *source, uLong sourceLen));
|
||||
int compress (Bytef *dest, uLongf *destLen,
|
||||
const Bytef *source, uLong sourceLen);
|
||||
/*
|
||||
Compresses the source buffer into the destination buffer. sourceLen is
|
||||
the byte length of the source buffer. Upon entry, destLen is the total size
|
||||
@ -1180,9 +1173,9 @@ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
|
||||
buffer.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
|
||||
int compress2 (Bytef *dest, uLongf *destLen,
|
||||
const Bytef *source, uLong sourceLen,
|
||||
int level));
|
||||
int level);
|
||||
/*
|
||||
Compresses the source buffer into the destination buffer. The level
|
||||
parameter has the same meaning as in deflateInit. sourceLen is the byte
|
||||
@ -1196,15 +1189,15 @@ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
|
||||
Z_STREAM_ERROR if the level parameter is invalid.
|
||||
*/
|
||||
|
||||
ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
|
||||
uLong compressBound (uLong sourceLen);
|
||||
/*
|
||||
compressBound() returns an upper bound on the compressed size after
|
||||
compress() or compress2() on sourceLen bytes. It would be used before a
|
||||
compress() or compress2() call to allocate the destination buffer.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
|
||||
const Bytef *source, uLong sourceLen));
|
||||
int uncompress (Bytef *dest, uLongf *destLen,
|
||||
const Bytef *source, uLongf sourceLen);
|
||||
/*
|
||||
Decompresses the source buffer into the destination buffer. sourceLen is
|
||||
the byte length of the source buffer. Upon entry, destLen is the total size
|
||||
@ -1233,7 +1226,7 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
|
||||
typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */
|
||||
|
||||
/*
|
||||
ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
|
||||
gzFile gzopen (const char *path, const char *mode);
|
||||
|
||||
Opens a gzip (.gz) file for reading or writing. The mode parameter is as
|
||||
in fopen ("rb" or "wb") but can also include a compression level ("wb9") or
|
||||
@ -1270,7 +1263,7 @@ ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
|
||||
file could not be opened.
|
||||
*/
|
||||
|
||||
ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
|
||||
gzFile gzdopen (int fd, const char *mode);
|
||||
/*
|
||||
gzdopen associates a gzFile with the file descriptor fd. File descriptors
|
||||
are obtained from calls like open, dup, creat, pipe or fileno (if the file
|
||||
@ -1293,7 +1286,7 @@ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
|
||||
will not detect if fd is invalid (unless fd is -1).
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
|
||||
int gzbuffer (gzFile file, unsigned size);
|
||||
/*
|
||||
Set the internal buffer size used by this library's functions. The
|
||||
default buffer size is 8192 bytes. This function must be called after
|
||||
@ -1310,7 +1303,7 @@ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
|
||||
too late.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
|
||||
int gzsetparams (gzFile file, int level, int strategy);
|
||||
/*
|
||||
Dynamically update the compression level or strategy. See the description
|
||||
of deflateInit2 for the meaning of these parameters.
|
||||
@ -1319,7 +1312,7 @@ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
|
||||
opened for writing.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
|
||||
int gzread (gzFile file, voidp buf, unsigned len);
|
||||
/*
|
||||
Reads the given number of uncompressed bytes from the compressed file. If
|
||||
the input file is not in gzip format, gzread copies the given number of
|
||||
@ -1347,15 +1340,15 @@ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
|
||||
len for end of file, or -1 for error.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
|
||||
voidpc buf, unsigned len));
|
||||
int gzwrite (gzFile file,
|
||||
voidpc buf, unsigned len);
|
||||
/*
|
||||
Writes the given number of uncompressed bytes into the compressed file.
|
||||
gzwrite returns the number of uncompressed bytes written or 0 in case of
|
||||
error.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
|
||||
int gzprintf Z_ARG((gzFile file, const char *format, ...));
|
||||
/*
|
||||
Converts, formats, and writes the arguments to the compressed file under
|
||||
control of the format string, as in fprintf. gzprintf returns the number of
|
||||
@ -1370,7 +1363,7 @@ ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
|
||||
zlibCompileFlags().
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
|
||||
int gzputs (gzFile file, const char *s);
|
||||
/*
|
||||
Writes the given null-terminated string to the compressed file, excluding
|
||||
the terminating null character.
|
||||
@ -1378,7 +1371,7 @@ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
|
||||
gzputs returns the number of characters written, or -1 in case of error.
|
||||
*/
|
||||
|
||||
ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
|
||||
char * gzgets (gzFile file, char *buf, int len);
|
||||
/*
|
||||
Reads bytes from the compressed file until len-1 characters are read, or a
|
||||
newline character is read and transferred to buf, or an end-of-file
|
||||
@ -1391,13 +1384,13 @@ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
|
||||
buf are indeterminate.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
|
||||
int gzputc (gzFile file, int c);
|
||||
/*
|
||||
Writes c, converted to an unsigned char, into the compressed file. gzputc
|
||||
returns the value that was written, or -1 in case of error.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
|
||||
int gzgetc (gzFile file);
|
||||
/*
|
||||
Reads one byte from the compressed file. gzgetc returns this byte or -1
|
||||
in case of end of file or error. This is implemented as a macro for speed.
|
||||
@ -1406,7 +1399,7 @@ ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
|
||||
points to has been clobbered or not.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
|
||||
int gzungetc (int c, gzFile file);
|
||||
/*
|
||||
Push one character back onto the stream to be read as the first character
|
||||
on the next read. At least one character of push-back is allowed.
|
||||
@ -1418,7 +1411,7 @@ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
|
||||
gzseek() or gzrewind().
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
|
||||
int gzflush (gzFile file, int flush);
|
||||
/*
|
||||
Flushes all pending output into the compressed file. The parameter flush
|
||||
is as in the deflate() function. The return value is the zlib error number
|
||||
@ -1434,8 +1427,8 @@ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
|
||||
z_off_t offset, int whence));
|
||||
z_off_t gzseek (gzFile file,
|
||||
z_off_t offset, int whence);
|
||||
|
||||
Sets the starting position for the next gzread or gzwrite on the given
|
||||
compressed file. The offset represents a number of bytes in the
|
||||
@ -1453,7 +1446,7 @@ ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
|
||||
would be before the current position.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
|
||||
int gzrewind (gzFile file);
|
||||
/*
|
||||
Rewinds the given file. This function is supported only for reading.
|
||||
|
||||
@ -1461,7 +1454,7 @@ ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
|
||||
z_off_t gztell (gzFile file);
|
||||
|
||||
Returns the starting position for the next gzread or gzwrite on the given
|
||||
compressed file. This position represents a number of bytes in the
|
||||
@ -1472,7 +1465,7 @@ ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
|
||||
z_off_t gzoffset (gzFile file);
|
||||
|
||||
Returns the current offset in the file being read or written. This offset
|
||||
includes the count of bytes that precede the gzip stream, for example when
|
||||
@ -1481,7 +1474,7 @@ ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
|
||||
for a progress indicator. On error, gzoffset() returns -1.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzeof OF((gzFile file));
|
||||
int gzeof (gzFile file);
|
||||
/*
|
||||
Returns true (1) if the end-of-file indicator has been set while reading,
|
||||
false (0) otherwise. Note that the end-of-file indicator is set only if the
|
||||
@ -1496,7 +1489,7 @@ ZEXTERN int ZEXPORT gzeof OF((gzFile file));
|
||||
has grown since the previous end of file was detected.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
|
||||
int gzdirect (gzFile file);
|
||||
/*
|
||||
Returns true (1) if file is being copied directly while reading, or false
|
||||
(0) if file is a gzip stream being decompressed.
|
||||
@ -1517,7 +1510,7 @@ ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
|
||||
gzip file reading and decompression, which may not be desired.)
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzclose OF((gzFile file));
|
||||
int gzclose (gzFile file);
|
||||
/*
|
||||
Flushes all pending output if necessary, closes the compressed file and
|
||||
deallocates the (de)compression state. Note that once file is closed, you
|
||||
@ -1530,8 +1523,8 @@ ZEXTERN int ZEXPORT gzclose OF((gzFile file));
|
||||
last read ended in the middle of a gzip stream, or Z_OK on success.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
|
||||
ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
|
||||
int gzclose_r (gzFile file);
|
||||
int gzclose_w (gzFile file);
|
||||
/*
|
||||
Same as gzclose(), but gzclose_r() is only for use when reading, and
|
||||
gzclose_w() is only for use when writing or appending. The advantage to
|
||||
@ -1542,7 +1535,7 @@ ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
|
||||
zlib library.
|
||||
*/
|
||||
|
||||
ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
|
||||
const char * gzerror (gzFile file, int *errnum);
|
||||
/*
|
||||
Returns the error message for the last error which occurred on the given
|
||||
compressed file. errnum is set to zlib error number. If an error occurred
|
||||
@ -1558,7 +1551,7 @@ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
|
||||
functions above that do not distinguish those cases in their return values.
|
||||
*/
|
||||
|
||||
ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
|
||||
void gzclearerr (gzFile file);
|
||||
/*
|
||||
Clears the error and end-of-file flags for file. This is analogous to the
|
||||
clearerr() function in stdio. This is useful for continuing to read a gzip
|
||||
@ -1575,7 +1568,7 @@ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
|
||||
library.
|
||||
*/
|
||||
|
||||
ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
|
||||
uint32_t adler32 (uint32_t adler, const uint8_t *buf, size_t len);
|
||||
/*
|
||||
Update a running Adler-32 checksum with the bytes buf[0..len-1] and
|
||||
return the updated checksum. If buf is Z_NULL, this function returns the
|
||||
@ -1595,8 +1588,8 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
|
||||
z_off_t len2));
|
||||
uLong adler32_combine (uLong adler1, uLong adler2,
|
||||
z_off_t len2);
|
||||
|
||||
Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
|
||||
and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
|
||||
@ -1606,7 +1599,7 @@ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
|
||||
negative, the result has no meaning or utility.
|
||||
*/
|
||||
|
||||
ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
|
||||
uLong crc32 (uLong crc, const Bytef *buf, uInt len);
|
||||
/*
|
||||
Update a running CRC-32 with the bytes buf[0..len-1] and return the
|
||||
updated CRC-32. If buf is Z_NULL, this function returns the required
|
||||
@ -1624,7 +1617,7 @@ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
|
||||
uLong crc32_combine (uLong crc1, uLong crc2, z_off_t len2);
|
||||
|
||||
Combine two CRC-32 check values into one. For two sequences of bytes,
|
||||
seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
|
||||
@ -1633,26 +1626,25 @@ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
|
||||
len2.
|
||||
*/
|
||||
|
||||
|
||||
/* various hacks, don't look :) */
|
||||
|
||||
/* deflateInit and inflateInit are macros to allow checking the zlib version
|
||||
* and the compiler's view of z_stream:
|
||||
*/
|
||||
ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
|
||||
const char *version, int stream_size));
|
||||
ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
|
||||
const char *version, int stream_size));
|
||||
ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
|
||||
int deflateInit_ (z_streamp strm, int level,
|
||||
const char *version, int stream_size);
|
||||
int inflateInit_ (z_streamp strm,
|
||||
const char *version, int stream_size);
|
||||
int deflateInit2_ (z_streamp strm, int level, int method,
|
||||
int windowBits, int memLevel,
|
||||
int strategy, const char *version,
|
||||
int stream_size));
|
||||
ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
|
||||
const char *version, int stream_size));
|
||||
ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
|
||||
int stream_size);
|
||||
int inflateInit2_ (z_streamp strm, int windowBits,
|
||||
const char *version, int stream_size);
|
||||
int inflateBackInit_ (z_streamp strm, int windowBits,
|
||||
unsigned char FAR *window,
|
||||
const char *version,
|
||||
int stream_size));
|
||||
int stream_size);
|
||||
#define deflateInit(strm, level) \
|
||||
deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
|
||||
#define inflateInit(strm) \
|
||||
@ -1676,7 +1668,7 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
|
||||
* behavior could change in the future, perhaps even capriciously. They can
|
||||
* only be used by the gzgetc() macro. You have been warned.
|
||||
*/
|
||||
ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
|
||||
int gzgetc_ (gzFile file); /* backward compatibility */
|
||||
#ifdef Z_PREFIX_SET
|
||||
# undef z_gzgetc
|
||||
# define z_gzgetc(g) \
|
||||
@ -1693,12 +1685,12 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
|
||||
* without large file support, _LFS64_LARGEFILE must also be true
|
||||
*/
|
||||
#ifdef Z_LARGE64
|
||||
ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
|
||||
ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
|
||||
ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
|
||||
ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
|
||||
ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
|
||||
ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
|
||||
gzFile gzopen64 (const char *, const char *);
|
||||
z_off64_t gzseek64 (gzFile, z_off64_t, int);
|
||||
z_off64_t gztell64 (gzFile);
|
||||
z_off64_t gzoffset64 (gzFile);
|
||||
uLong adler32_combine64 (uLong, uLong, z_off64_t);
|
||||
uLong crc32_combine64 (uLong, uLong, z_off64_t);
|
||||
#endif
|
||||
|
||||
#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)
|
||||
@ -1718,26 +1710,26 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
|
||||
# define crc32_combine crc32_combine64
|
||||
# endif
|
||||
# ifndef Z_LARGE64
|
||||
ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
|
||||
ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
|
||||
ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
|
||||
ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
|
||||
ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
|
||||
ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
|
||||
gzFile gzopen64 (const char *, const char *);
|
||||
z_off_t gzseek64 (gzFile, z_off_t, int);
|
||||
z_off_t gztell64 (gzFile);
|
||||
z_off_t gzoffset64 (gzFile);
|
||||
uLong adler32_combine64 (uLong, uLong, z_off_t);
|
||||
uLong crc32_combine64 (uLong, uLong, z_off_t);
|
||||
# endif
|
||||
#else
|
||||
ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *));
|
||||
ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
|
||||
ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
|
||||
ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
|
||||
ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
|
||||
ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
|
||||
gzFile gzopen (const char *, const char *);
|
||||
z_off_t gzseek (gzFile, z_off_t, int);
|
||||
z_off_t gztell (gzFile);
|
||||
z_off_t gzoffset (gzFile);
|
||||
uLong adler32_combine (uLong, uLong, z_off_t);
|
||||
uLong crc32_combine (uLong, uLong, z_off_t);
|
||||
#endif
|
||||
|
||||
#else /* Z_SOLO */
|
||||
|
||||
ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
|
||||
ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
|
||||
uLong adler32_combine (uLong, uLong, z_off_t);
|
||||
uLong crc32_combine (uLong, uLong, z_off_t);
|
||||
|
||||
#endif /* !Z_SOLO */
|
||||
|
||||
@ -1747,19 +1739,21 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
|
||||
#endif
|
||||
|
||||
/* undocumented functions */
|
||||
ZEXTERN const char * ZEXPORT zError OF((int));
|
||||
ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp));
|
||||
ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void));
|
||||
ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int));
|
||||
ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp));
|
||||
ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp));
|
||||
const char * zError (int);
|
||||
int inflateSyncPoint (z_streamp);
|
||||
|
||||
const uint32_t * get_crc_table(void);
|
||||
int inflateUndermine (z_streamp, int);
|
||||
int inflateValidate (z_streamp, int);
|
||||
int inflateResetKeep (z_streamp);
|
||||
int deflateResetKeep (z_streamp);
|
||||
#if defined(_WIN32) && !defined(Z_SOLO)
|
||||
ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path,
|
||||
const char *mode));
|
||||
gzFile gzopen_w (const wchar_t *path,
|
||||
const char *mode);
|
||||
#endif
|
||||
#if defined(STDC) || defined(Z_HAVE_STDARG_H)
|
||||
# ifndef Z_SOLO
|
||||
ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file,
|
||||
int gzvprintf Z_ARG((gzFile file,
|
||||
const char *format,
|
||||
va_list va));
|
||||
# endif
|
||||
|
@ -109,6 +109,8 @@ extern char z_errmsg[10][21]; /* indexed by 2-zlib_error */
|
||||
|
||||
#if defined(VAXC) || defined(VMS)
|
||||
# define OS_CODE 0x02
|
||||
# define F_OPEN(name, mode) \
|
||||
fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
|
||||
#endif
|
||||
|
||||
#if defined(ATARI) || defined(atarist)
|
||||
@ -174,14 +176,18 @@ extern char z_errmsg[10][21]; /* indexed by 2-zlib_error */
|
||||
/* provide prototypes for these when building zlib without LFS */
|
||||
#if !defined(_WIN32) && \
|
||||
(!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)
|
||||
ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
|
||||
ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
|
||||
uLong adler32_combine64 (uLong, uLong, z_off_t);
|
||||
uLong crc32_combine64 (uLong, uLong, z_off_t);
|
||||
#endif
|
||||
|
||||
/* common defaults */
|
||||
|
||||
#ifndef OS_CODE
|
||||
# define OS_CODE 0x03 /* assume Unix */
|
||||
#endif
|
||||
|
||||
#ifndef F_OPEN
|
||||
# define F_OPEN(name, mode) fopen((name), (mode))
|
||||
#endif
|
||||
|
||||
/* functions */
|
||||
@ -210,35 +216,23 @@ extern char z_errmsg[10][21]; /* indexed by 2-zlib_error */
|
||||
# define zmemzero(dest, len) memset(dest, 0, len)
|
||||
# endif
|
||||
#else
|
||||
void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
|
||||
int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
|
||||
void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));
|
||||
void ZLIB_INTERNAL zmemcpy (Bytef* dest, const Bytef* source, uInt len);
|
||||
int ZLIB_INTERNAL zmemcmp (const Bytef* s1, const Bytef* s2, uInt len);
|
||||
void ZLIB_INTERNAL zmemzero (Bytef* dest, uInt len);
|
||||
#endif
|
||||
|
||||
/* Diagnostic functions */
|
||||
#ifdef DEBUG
|
||||
# include <stdio.h>
|
||||
extern int ZLIB_INTERNAL z_verbose;
|
||||
extern void ZLIB_INTERNAL z_error OF((char *m));
|
||||
# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
|
||||
# define Trace(x) {if (z_verbose>=0) fprintf x ;}
|
||||
# define Tracev(x) {if (z_verbose>0) fprintf x ;}
|
||||
# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
|
||||
# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
|
||||
# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
|
||||
#else
|
||||
# define Assert(cond,msg)
|
||||
# define Trace(x)
|
||||
# define Tracev(x)
|
||||
# define Tracevv(x)
|
||||
# define Tracec(c,x)
|
||||
# define Tracecv(c,x)
|
||||
#endif
|
||||
|
||||
#ifndef Z_SOLO
|
||||
voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
|
||||
unsigned size));
|
||||
void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr));
|
||||
voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items,
|
||||
unsigned size);
|
||||
void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr);
|
||||
#endif
|
||||
|
||||
#define ZALLOC(strm, items, size) \
|
||||
|
@ -38,29 +38,99 @@ enum CodePage
|
||||
CODEPAGE_UTF8 = 65001 /* CP_UTF8 */
|
||||
};
|
||||
|
||||
/**
|
||||
* utf8_conv_utf32:
|
||||
*
|
||||
* Simple implementation. Assumes the sequence is
|
||||
* properly synchronized and terminated.
|
||||
**/
|
||||
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
|
||||
const char *in, size_t in_size);
|
||||
|
||||
/**
|
||||
* utf16_conv_utf8:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
||||
const uint16_t *in, size_t in_size);
|
||||
|
||||
/**
|
||||
* utf8len:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
size_t utf8len(const char *string);
|
||||
|
||||
/**
|
||||
* utf8cpy:
|
||||
*
|
||||
* Acts mostly like strlcpy.
|
||||
*
|
||||
* Copies the given number of UTF-8 characters,
|
||||
* but at most @d_len bytes.
|
||||
*
|
||||
* Always NULL terminates. Does not copy half a character.
|
||||
* @s is assumed valid UTF-8.
|
||||
* Use only if @chars is considerably less than @d_len.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls memcpy
|
||||
*
|
||||
* @return Number of bytes.
|
||||
**/
|
||||
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars);
|
||||
|
||||
/**
|
||||
* utf8skip:
|
||||
*
|
||||
* Leaf function
|
||||
**/
|
||||
const char *utf8skip(const char *str, size_t chars);
|
||||
|
||||
/**
|
||||
* utf8_walk:
|
||||
*
|
||||
* Does not validate the input.
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return Returns garbage if it's not UTF-8.
|
||||
**/
|
||||
uint32_t utf8_walk(const char **string);
|
||||
|
||||
/**
|
||||
* utf16_to_char_string:
|
||||
**/
|
||||
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len);
|
||||
|
||||
char* utf8_to_local_string_alloc(const char *str);
|
||||
/**
|
||||
* utf8_to_local_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *utf8_to_local_string_alloc(const char *str);
|
||||
|
||||
char* local_to_utf8_string_alloc(const char *str);
|
||||
/**
|
||||
* local_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *local_to_utf8_string_alloc(const char *str);
|
||||
|
||||
wchar_t* utf8_to_utf16_string_alloc(const char *str);
|
||||
/**
|
||||
* utf8_to_utf16_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
wchar_t *utf8_to_utf16_string_alloc(const char *str);
|
||||
|
||||
char* utf16_to_utf8_string_alloc(const wchar_t *str);
|
||||
/**
|
||||
* utf16_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *utf16_to_utf8_string_alloc(const wchar_t *str);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
|
@ -51,6 +51,28 @@ enum
|
||||
RARCH_FILE_UNSUPPORTED
|
||||
};
|
||||
|
||||
struct path_linked_list
|
||||
{
|
||||
char *path;
|
||||
struct path_linked_list *next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new linked list with one item in it
|
||||
* The path on this item will be set to NULL
|
||||
**/
|
||||
struct path_linked_list* path_linked_list_new(void);
|
||||
|
||||
/* Free the entire linked list */
|
||||
void path_linked_list_free(struct path_linked_list *in_path_linked_list);
|
||||
|
||||
/**
|
||||
* Add a node to the linked list with this path
|
||||
* If the first node's path if it's not yet set,
|
||||
* set this instead
|
||||
**/
|
||||
void path_linked_list_add_path(struct path_linked_list *in_path_linked_list, char *path);
|
||||
|
||||
/**
|
||||
* path_is_compressed_file:
|
||||
* @path : path
|
||||
@ -81,12 +103,12 @@ bool path_is_compressed_file(const char *path);
|
||||
* path_get_archive_delim:
|
||||
* @path : path
|
||||
*
|
||||
* Gets delimiter of an archive file. Only the first '#'
|
||||
* Find delimiter of an archive file. Only the first '#'
|
||||
* after a compression extension is considered.
|
||||
*
|
||||
* Returns: pointer to the delimiter in the path if it contains
|
||||
* a compressed file, otherwise NULL.
|
||||
*/
|
||||
* @return pointer to the delimiter in the path if it contains
|
||||
* a path inside a compressed file, otherwise NULL.
|
||||
**/
|
||||
const char *path_get_archive_delim(const char *path);
|
||||
|
||||
/**
|
||||
@ -96,10 +118,28 @@ const char *path_get_archive_delim(const char *path);
|
||||
* Gets extension of file. Only '.'s
|
||||
* after the last slash are considered.
|
||||
*
|
||||
* Returns: extension part from the path.
|
||||
*/
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls string_is_empty()
|
||||
* - calls strrchr
|
||||
*
|
||||
* @return extension part from the path.
|
||||
**/
|
||||
const char *path_get_extension(const char *path);
|
||||
|
||||
/**
|
||||
* path_get_extension_mutable:
|
||||
* @path : path
|
||||
*
|
||||
* Specialized version of path_get_extension(). Return
|
||||
* value is mutable.
|
||||
*
|
||||
* Gets extension of file. Only '.'s
|
||||
* after the last slash are considered.
|
||||
*
|
||||
* @return extension part from the path.
|
||||
**/
|
||||
char *path_get_extension_mutable(const char *path);
|
||||
|
||||
/**
|
||||
* path_remove_extension:
|
||||
* @path : path
|
||||
@ -108,7 +148,10 @@ const char *path_get_extension(const char *path);
|
||||
* text after and including the last '.'.
|
||||
* Only '.'s after the last slash are considered.
|
||||
*
|
||||
* Returns:
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strrchr
|
||||
*
|
||||
* @return
|
||||
* 1) If path has an extension, returns path with the
|
||||
* extension removed.
|
||||
* 2) If there is no extension, returns NULL.
|
||||
@ -122,9 +165,26 @@ char *path_remove_extension(char *path);
|
||||
*
|
||||
* Get basename from @path.
|
||||
*
|
||||
* Returns: basename from path.
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls path_get_archive_delim()
|
||||
* - can call find_last_slash() once if it returns NULL
|
||||
*
|
||||
* @return basename from path.
|
||||
**/
|
||||
const char *path_basename(const char *path);
|
||||
|
||||
/**
|
||||
* path_basename_nocompression:
|
||||
* @path : path
|
||||
*
|
||||
* Specialized version of path_basename().
|
||||
* Get basename from @path.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls find_last_slash()
|
||||
*
|
||||
* @return basename from path.
|
||||
**/
|
||||
const char *path_basename_nocompression(const char *path);
|
||||
|
||||
/**
|
||||
@ -139,12 +199,13 @@ void path_basedir(char *path);
|
||||
/**
|
||||
* path_parent_dir:
|
||||
* @path : path
|
||||
* @len : length of @path
|
||||
*
|
||||
* Extracts parent directory by mutating path.
|
||||
* Assumes that path is a directory. Keeps trailing '/'.
|
||||
* If the path was already at the root directory, returns empty string
|
||||
**/
|
||||
void path_parent_dir(char *path);
|
||||
void path_parent_dir(char *path, size_t len);
|
||||
|
||||
/**
|
||||
* path_resolve_realpath:
|
||||
@ -156,7 +217,7 @@ void path_parent_dir(char *path);
|
||||
*
|
||||
* Relative paths are rebased on the current working dir.
|
||||
*
|
||||
* Returns: @buf if successful, NULL otherwise.
|
||||
* @return @buf if successful, NULL otherwise.
|
||||
* Note: Not implemented on consoles
|
||||
* Note: Symlinks are only resolved on Unix-likes
|
||||
* Note: The current working dir might not be what you expect,
|
||||
@ -178,8 +239,11 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks);
|
||||
* Both @path and @base are assumed to be absolute paths without "." or "..".
|
||||
*
|
||||
* E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp
|
||||
*
|
||||
* @return Length of the string copied into @out
|
||||
**/
|
||||
size_t path_relative_to(char *out, const char *path, const char *base, size_t size);
|
||||
size_t path_relative_to(char *out, const char *path, const char *base,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* path_is_absolute:
|
||||
@ -187,7 +251,7 @@ size_t path_relative_to(char *out, const char *path, const char *base, size_t si
|
||||
*
|
||||
* Checks if @path is an absolute path or a relative path.
|
||||
*
|
||||
* Returns: true if path is absolute, false if path is relative.
|
||||
* @return true if path is absolute, false if path is relative.
|
||||
**/
|
||||
bool path_is_absolute(const char *path);
|
||||
|
||||
@ -211,8 +275,15 @@ bool path_is_absolute(const char *path);
|
||||
* out_path = "/foo/bar/baz/boo.asm"
|
||||
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" =>
|
||||
* out_path = "/foo/bar/baz/boo"
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strlcpy 2x
|
||||
* - calls strrchr
|
||||
* - calls strlcat
|
||||
*
|
||||
* @return Length of the string copied into @out
|
||||
*/
|
||||
void 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);
|
||||
|
||||
/**
|
||||
@ -226,6 +297,12 @@ void fill_pathname(char *out_path, const char *in_path,
|
||||
*
|
||||
* E.g.:
|
||||
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls rtime_localtime()
|
||||
* - Calls strftime
|
||||
* - Calls strlcat
|
||||
*
|
||||
**/
|
||||
size_t fill_dated_filename(char *out_filename,
|
||||
const char *ext, size_t size);
|
||||
@ -242,34 +319,33 @@ size_t fill_dated_filename(char *out_filename,
|
||||
*
|
||||
* E.g.:
|
||||
* out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls time
|
||||
* - Calls rtime_localtime()
|
||||
* - Calls strlcpy 2x
|
||||
* - Calls string_is_empty()
|
||||
* - Calls strftime
|
||||
* - Calls strlcat
|
||||
*
|
||||
* @return Length of the string copied into @out_path
|
||||
**/
|
||||
void 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);
|
||||
|
||||
/**
|
||||
* fill_pathname_noext:
|
||||
* @out_path : output path
|
||||
* @in_path : input path
|
||||
* @replace : what to replace
|
||||
* @size : buffer size of output path
|
||||
*
|
||||
* Appends a filename extension 'replace' to 'in_path', and outputs
|
||||
* result in 'out_path'.
|
||||
*
|
||||
* Assumes in_path has no extension. If an extension is still
|
||||
* present in 'in_path', it will be ignored.
|
||||
*
|
||||
*/
|
||||
size_t fill_pathname_noext(char *out_path, const char *in_path,
|
||||
const char *replace, size_t size);
|
||||
|
||||
/**
|
||||
* find_last_slash:
|
||||
* @str : input path
|
||||
* @str : path
|
||||
* @size : size of path
|
||||
*
|
||||
* Gets a pointer to the last slash in the input path.
|
||||
* Find last slash in path. Tries to find
|
||||
* a backslash on Windows too which takes precedence
|
||||
* over regular slash.
|
||||
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strrchr
|
||||
*
|
||||
* Returns: a pointer to the last slash in the input path.
|
||||
* @return pointer to last slash/backslash found in @str.
|
||||
**/
|
||||
char *find_last_slash(const char *str);
|
||||
|
||||
@ -289,6 +365,11 @@ char *find_last_slash(const char *str);
|
||||
*
|
||||
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
|
||||
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls fill_pathname_slash()
|
||||
* - Calls path_basename()
|
||||
* - Calls strlcpy 2x
|
||||
**/
|
||||
size_t fill_pathname_dir(char *in_dir, const char *in_basename,
|
||||
const char *replace, size_t size);
|
||||
@ -300,16 +381,15 @@ size_t fill_pathname_dir(char *in_dir, const char *in_basename,
|
||||
* @size : size of output path
|
||||
*
|
||||
* Copies basename of @in_path into @out_path.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls path_basename()
|
||||
* - Calls strlcpy
|
||||
*
|
||||
* @return length of the string copied into @out
|
||||
**/
|
||||
size_t fill_pathname_base(char *out_path, const char *in_path, size_t size);
|
||||
|
||||
void fill_pathname_base_noext(char *out_dir,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
size_t fill_pathname_base_ext(char *out,
|
||||
const char *in_path, const char *ext,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* fill_pathname_basedir:
|
||||
* @out_dir : output directory
|
||||
@ -319,12 +399,13 @@ size_t fill_pathname_base_ext(char *out,
|
||||
* Copies base directory of @in_path into @out_path.
|
||||
* If in_path is a path without any slashes (relative current directory),
|
||||
* @out_path will get path "./".
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strlcpy
|
||||
* - Calls path_basedir()
|
||||
**/
|
||||
void fill_pathname_basedir(char *out_path, const char *in_path, size_t size);
|
||||
|
||||
void fill_pathname_basedir_noext(char *out_dir,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
/**
|
||||
* fill_pathname_parent_dir_name:
|
||||
* @out_dir : output directory
|
||||
@ -333,7 +414,13 @@ void fill_pathname_basedir_noext(char *out_dir,
|
||||
*
|
||||
* Copies only the parent directory name of @in_dir into @out_dir.
|
||||
* The two buffers must not overlap. Removes trailing '/'.
|
||||
* Returns true on success, false if a slash was not found in the path.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strdup
|
||||
* - Calls find_last_slash() x times
|
||||
* - Can call strlcpy
|
||||
*
|
||||
* @return true on success, false if a slash was not found in the path.
|
||||
**/
|
||||
bool fill_pathname_parent_dir_name(char *out_dir,
|
||||
const char *in_dir, size_t size);
|
||||
@ -347,6 +434,11 @@ bool fill_pathname_parent_dir_name(char *out_dir,
|
||||
* Copies parent directory of @in_dir into @out_dir.
|
||||
* Assumes @in_dir is a directory. Keeps trailing '/'.
|
||||
* If the path was already at the root directory, @out_dir will be an empty string.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Can call strlcpy if (@out_dir != @in_dir)
|
||||
* - Calls strlen if (@out_dir == @in_dir)
|
||||
* - Calls path_parent_dir()
|
||||
**/
|
||||
void fill_pathname_parent_dir(char *out_dir,
|
||||
const char *in_dir, size_t size);
|
||||
@ -376,28 +468,49 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
|
||||
* Joins a directory (@dir) and path (@path) together.
|
||||
* Makes sure not to get two consecutive slashes
|
||||
* between directory and path.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strlcpy at least once
|
||||
* - calls fill_pathname_slash()
|
||||
*
|
||||
* Deprecated. Use fill_pathname_join_special() instead
|
||||
* if you can ensure @dir != @out_path
|
||||
*
|
||||
* @return Length of the string copied into @out_path
|
||||
**/
|
||||
size_t fill_pathname_join(char *out_path, const char *dir,
|
||||
const char *path, size_t size);
|
||||
|
||||
/**
|
||||
* fill_pathname_join_special:
|
||||
* @out_path : output path
|
||||
* @dir : directory. Cannot be identical to @out_path
|
||||
* @path : path
|
||||
* @size : size of output path
|
||||
*
|
||||
*
|
||||
* Specialized version of fill_pathname_join.
|
||||
* Unlike fill_pathname_join(),
|
||||
* @dir and @out_path CANNOT be identical.
|
||||
*
|
||||
* Joins a directory (@dir) and path (@path) together.
|
||||
* Makes sure not to get two consecutive slashes
|
||||
* between directory and path.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strlcpy 2x
|
||||
* - calls find_last_slash()
|
||||
*
|
||||
* @return Length of the string copied into @out_path
|
||||
**/
|
||||
size_t fill_pathname_join_special(char *out_path,
|
||||
const char *dir, const char *path, size_t size);
|
||||
|
||||
size_t fill_pathname_join_special_ext(char *out_path,
|
||||
const char *dir, const char *path,
|
||||
const char *last, const char *ext,
|
||||
size_t size);
|
||||
|
||||
size_t fill_pathname_join_concat_noext(char *out_path,
|
||||
const char *dir, const char *path,
|
||||
const char *concat,
|
||||
size_t size);
|
||||
|
||||
size_t fill_pathname_join_concat(char *out_path,
|
||||
const char *dir, const char *path,
|
||||
const char *concat,
|
||||
size_t size);
|
||||
|
||||
void fill_pathname_join_noext(char *out_path,
|
||||
const char *dir, const char *path, size_t size);
|
||||
|
||||
/**
|
||||
* fill_pathname_join_delim:
|
||||
* @out_path : output path
|
||||
@ -408,45 +521,57 @@ void fill_pathname_join_noext(char *out_path,
|
||||
*
|
||||
* Joins a directory (@dir) and path (@path) together
|
||||
* using the given delimiter (@delim).
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - can call strlen
|
||||
* - can call strlcpy
|
||||
* - can call strlcat
|
||||
**/
|
||||
size_t fill_pathname_join_delim(char *out_path, const char *dir,
|
||||
const char *path, const char delim, size_t size);
|
||||
|
||||
size_t fill_pathname_join_delim_concat(char *out_path, const char *dir,
|
||||
const char *path, const char delim, const char *concat,
|
||||
size_t size);
|
||||
size_t fill_pathname_expand_special(char *out_path,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
size_t fill_pathname_abbreviate_special(char *out_path,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
/**
|
||||
* fill_short_pathname_representation:
|
||||
* @out_rep : output representation
|
||||
* @in_path : input path
|
||||
* @size : size of output representation
|
||||
* fill_pathname_abbreviated_or_relative:
|
||||
*
|
||||
* Generates a short representation of path. It should only
|
||||
* be used for displaying the result; the output representation is not
|
||||
* binding in any meaningful way (for a normal path, this is the same as basename)
|
||||
* In case of more complex URLs, this should cut everything except for
|
||||
* the main image file.
|
||||
* Fills the supplied path with either the abbreviated path or
|
||||
* the relative path, which ever one has less depth / number of slashes
|
||||
*
|
||||
* E.g.: "/path/to/game.img" -> game.img
|
||||
* "/path/to/myarchive.7z#folder/to/game.img" -> game.img
|
||||
*/
|
||||
size_t fill_short_pathname_representation(char* out_rep,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
void fill_short_pathname_representation_noext(char* out_rep,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
void fill_pathname_expand_special(char *out_path,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
void fill_pathname_abbreviate_special(char *out_path,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
void fill_pathname_abbreviated_or_relative(char *out_path, const char *in_refpath, const char *in_path, size_t size);
|
||||
* If lengths of abbreviated and relative paths are the same,
|
||||
* the relative path will be used
|
||||
* @in_path can be an absolute, relative or abbreviated path
|
||||
*
|
||||
* @return Length of the string copied into @out_path
|
||||
**/
|
||||
size_t fill_pathname_abbreviated_or_relative(char *out_path,
|
||||
const char *in_refpath, const char *in_path, size_t size);
|
||||
|
||||
/**
|
||||
* pathname_conform_slashes_to_os:
|
||||
*
|
||||
* @path : path
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* pathname_make_slashes_portable:
|
||||
* @path : path
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Change all slashes to forward so they are more
|
||||
* portable between Windows and Linux
|
||||
**/
|
||||
void pathname_make_slashes_portable(char *path);
|
||||
|
||||
/**
|
||||
@ -464,8 +589,8 @@ void path_basedir_wrapper(char *path);
|
||||
*
|
||||
* Checks if character (@c) is a slash.
|
||||
*
|
||||
* Returns: true (1) if character is a slash, otherwise false (0).
|
||||
*/
|
||||
* @return true if character is a slash, otherwise false.
|
||||
**/
|
||||
#ifdef _WIN32
|
||||
#define PATH_CHAR_IS_SLASH(c) (((c) == '/') || ((c) == '\\'))
|
||||
#else
|
||||
@ -477,8 +602,8 @@ void path_basedir_wrapper(char *path);
|
||||
*
|
||||
* Gets the default slash separator.
|
||||
*
|
||||
* Returns: default slash separator.
|
||||
*/
|
||||
* @return default slash separator.
|
||||
**/
|
||||
#ifdef _WIN32
|
||||
#define PATH_DEFAULT_SLASH() "\\"
|
||||
#define PATH_DEFAULT_SLASH_C() '\\'
|
||||
@ -494,8 +619,13 @@ void path_basedir_wrapper(char *path);
|
||||
*
|
||||
* Assumes path is a directory. Appends a slash
|
||||
* if not already there.
|
||||
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls find_last_slash()
|
||||
* - 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);
|
||||
@ -509,7 +639,16 @@ void fill_pathname_home_dir(char *buf, size_t size);
|
||||
*
|
||||
* Create directory on filesystem.
|
||||
*
|
||||
* Returns: true (1) if directory could be created, otherwise false (0).
|
||||
* Recursive function.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strdup
|
||||
* - Calls path_parent_dir()
|
||||
* - Calls strcmp
|
||||
* - Calls path_is_directory()
|
||||
* - Calls path_mkdir()
|
||||
*
|
||||
* @return true if directory could be created, otherwise false.
|
||||
**/
|
||||
bool path_mkdir(const char *dir);
|
||||
|
||||
@ -519,10 +658,15 @@ bool path_mkdir(const char *dir);
|
||||
*
|
||||
* Checks if path is a directory.
|
||||
*
|
||||
* Returns: true (1) if path is a directory, otherwise false (0).
|
||||
* @return true if path is a directory, otherwise false.
|
||||
*/
|
||||
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);
|
||||
|
@ -69,7 +69,7 @@ extern "C" {
|
||||
# endif
|
||||
# endif
|
||||
# else
|
||||
# if defined(__GNUC__) && __GNUC__ >= 4 && !defined(__PS3__)
|
||||
# if defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define RETRO_API RETRO_CALLCONV __attribute__((__visibility__("default")))
|
||||
# else
|
||||
# define RETRO_API RETRO_CALLCONV
|
||||
@ -283,6 +283,15 @@ enum retro_language
|
||||
RETRO_LANGUAGE_HEBREW = 21,
|
||||
RETRO_LANGUAGE_ASTURIAN = 22,
|
||||
RETRO_LANGUAGE_FINNISH = 23,
|
||||
RETRO_LANGUAGE_INDONESIAN = 24,
|
||||
RETRO_LANGUAGE_SWEDISH = 25,
|
||||
RETRO_LANGUAGE_UKRAINIAN = 26,
|
||||
RETRO_LANGUAGE_CZECH = 27,
|
||||
RETRO_LANGUAGE_CATALAN_VALENCIA = 28,
|
||||
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) */
|
||||
@ -920,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 * --
|
||||
@ -1722,6 +1729,127 @@ enum retro_mod
|
||||
* Must be called in retro_set_environment().
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_SET_VARIABLE 70
|
||||
/* const struct retro_variable * --
|
||||
* Allows an implementation to notify the frontend
|
||||
* that a core option value has changed.
|
||||
*
|
||||
* retro_variable::key and retro_variable::value
|
||||
* must match strings that have been set previously
|
||||
* via one of the following:
|
||||
*
|
||||
* - RETRO_ENVIRONMENT_SET_VARIABLES
|
||||
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS
|
||||
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL
|
||||
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2
|
||||
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL
|
||||
*
|
||||
* After changing a core option value via this
|
||||
* callback, RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE
|
||||
* will return true.
|
||||
*
|
||||
* If data is NULL, no changes will be registered
|
||||
* and the callback will return true; an
|
||||
* implementation may therefore pass NULL in order
|
||||
* to test whether the callback is supported.
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_GET_THROTTLE_STATE (71 | RETRO_ENVIRONMENT_EXPERIMENTAL)
|
||||
/* struct retro_throttle_state * --
|
||||
* Allows an implementation to get details on the actual rate
|
||||
* the frontend is attempting to call retro_run().
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_GET_SAVESTATE_CONTEXT (72 | RETRO_ENVIRONMENT_EXPERIMENTAL)
|
||||
/* int * --
|
||||
* Tells the core about the context the frontend is asking for savestate.
|
||||
* (see enum retro_savestate_context)
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT (73 | RETRO_ENVIRONMENT_EXPERIMENTAL)
|
||||
/* struct retro_hw_render_context_negotiation_interface * --
|
||||
* Before calling SET_HW_RNEDER_CONTEXT_NEGOTIATION_INTERFACE, a core can query
|
||||
* which version of the interface is supported.
|
||||
*
|
||||
* Frontend looks at interface_type and returns the maximum supported
|
||||
* context negotiation interface version.
|
||||
* If the interface_type is not supported or recognized by the frontend, a version of 0
|
||||
* must be returned in interface_version and true is returned by frontend.
|
||||
*
|
||||
* If this environment call returns true with interface_version greater than 0,
|
||||
* a core can always use a negotiation interface version larger than what the frontend returns, but only
|
||||
* earlier versions of the interface will be used by the frontend.
|
||||
* A frontend must not reject a negotiation interface version that is larger than
|
||||
* what the frontend supports. Instead, the frontend will use the older entry points that it recognizes.
|
||||
* If this is incompatible with a particular core's requirements, it can error out early.
|
||||
*
|
||||
* Backwards compatibility note:
|
||||
* This environment call was introduced after Vulkan v1 context negotiation.
|
||||
* If this environment call is not supported by frontend - i.e. the environment call returns false -
|
||||
* only Vulkan v1 context negotiation is supported (if Vulkan HW rendering is supported at all).
|
||||
* If a core uses Vulkan negotiation interface with version > 1, negotiation may fail unexpectedly.
|
||||
* All future updates to the context negotiation interface implies that frontend must 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 */
|
||||
|
||||
/* File paths:
|
||||
@ -2678,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
|
||||
};
|
||||
@ -2935,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.
|
||||
@ -2959,6 +3189,35 @@ enum retro_pixel_format
|
||||
RETRO_PIXEL_FORMAT_UNKNOWN = INT_MAX
|
||||
};
|
||||
|
||||
enum retro_savestate_context
|
||||
{
|
||||
/* Standard savestate written to disk. */
|
||||
RETRO_SAVESTATE_CONTEXT_NORMAL = 0,
|
||||
|
||||
/* Savestate where you are guaranteed that the same instance will load the save state.
|
||||
* You can store internal pointers to code or data.
|
||||
* It's still a full serialization and deserialization, and could be loaded or saved at any time.
|
||||
* It won't be written to disk or sent over the network.
|
||||
*/
|
||||
RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE = 1,
|
||||
|
||||
/* Savestate where you are guaranteed that the same emulator binary will load that savestate.
|
||||
* You can skip anything that would slow down saving or loading state but you can not store internal pointers.
|
||||
* It won't be written to disk or sent over the network.
|
||||
* Example: "Second Instance" runahead
|
||||
*/
|
||||
RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_BINARY = 2,
|
||||
|
||||
/* Savestate used within a rollback netplay feature.
|
||||
* You should skip anything that would unnecessarily increase bandwidth usage.
|
||||
* It won't be written to disk but it will be sent over the network.
|
||||
*/
|
||||
RETRO_SAVESTATE_CONTEXT_ROLLBACK_NETPLAY = 3,
|
||||
|
||||
/* Ensure sizeof() == sizeof(int). */
|
||||
RETRO_SAVESTATE_CONTEXT_UNKNOWN = INT_MAX
|
||||
};
|
||||
|
||||
struct retro_message
|
||||
{
|
||||
const char *msg; /* Message to be displayed. */
|
||||
@ -3430,6 +3689,10 @@ struct retro_core_option_definition
|
||||
const char *default_value;
|
||||
};
|
||||
|
||||
#ifdef __PS3__
|
||||
#undef local
|
||||
#endif
|
||||
|
||||
struct retro_core_options_intl
|
||||
{
|
||||
/* Pointer to an array of retro_core_option_definition structs
|
||||
@ -3667,6 +3930,326 @@ struct retro_fastforwarding_override
|
||||
bool inhibit_toggle;
|
||||
};
|
||||
|
||||
/* During normal operation. Rate will be equal to the core's internal FPS. */
|
||||
#define RETRO_THROTTLE_NONE 0
|
||||
|
||||
/* While paused or stepping single frames. Rate will be 0. */
|
||||
#define RETRO_THROTTLE_FRAME_STEPPING 1
|
||||
|
||||
/* During fast forwarding.
|
||||
* Rate will be 0 if not specifically limited to a maximum speed. */
|
||||
#define RETRO_THROTTLE_FAST_FORWARD 2
|
||||
|
||||
/* During slow motion. Rate will be less than the core's internal FPS. */
|
||||
#define RETRO_THROTTLE_SLOW_MOTION 3
|
||||
|
||||
/* While rewinding recorded save states. Rate can vary depending on the rewind
|
||||
* speed or be 0 if the frontend is not aiming for a specific rate. */
|
||||
#define RETRO_THROTTLE_REWINDING 4
|
||||
|
||||
/* While vsync is active in the video driver and the target refresh rate is
|
||||
* lower than the core's internal FPS. Rate is the target refresh rate. */
|
||||
#define RETRO_THROTTLE_VSYNC 5
|
||||
|
||||
/* When the frontend does not throttle in any way. Rate will be 0.
|
||||
* An example could be if no vsync or audio output is active. */
|
||||
#define RETRO_THROTTLE_UNBLOCKED 6
|
||||
|
||||
struct retro_throttle_state
|
||||
{
|
||||
/* The current throttling mode. Should be one of the values above. */
|
||||
unsigned mode;
|
||||
|
||||
/* How many times per second the frontend aims to call retro_run.
|
||||
* Depending on the mode, it can be 0 if there is no known fixed rate.
|
||||
* This won't be accurate if the total processing time of the core and
|
||||
* the frontend is longer than what is available for one frame. */
|
||||
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
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (memalign.h).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (retro_common.h).
|
||||
@ -34,4 +34,3 @@ in a public API, you may need this.
|
||||
#include <compat/msvc.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -49,6 +49,10 @@
|
||||
#include <compat/msvc.h>
|
||||
#endif
|
||||
|
||||
#ifdef IOS
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count)
|
||||
{
|
||||
uint32_t i;
|
||||
@ -74,14 +78,29 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
|
||||
return false;
|
||||
}
|
||||
|
||||
static INLINE bool bits_any_different(uint32_t *a, uint32_t *b, uint32_t count)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (a[i] != b[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef PATH_MAX_LENGTH
|
||||
#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS) || defined(__PS3__)
|
||||
#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(__PSL1GHT__) || defined(__PS3__)
|
||||
#define PATH_MAX_LENGTH 512
|
||||
#else
|
||||
#define PATH_MAX_LENGTH 4096
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NAME_MAX_LENGTH
|
||||
#define NAME_MAX_LENGTH 256
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2015 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rthreads.h).
|
||||
@ -36,6 +36,10 @@ typedef struct sthread sthread_t;
|
||||
typedef struct slock slock_t;
|
||||
typedef struct scond scond_t;
|
||||
|
||||
#ifdef HAVE_THREAD_STORAGE
|
||||
typedef unsigned sthread_tls_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sthread_create:
|
||||
* @start_routine : thread entry callback function
|
||||
@ -48,6 +52,23 @@ typedef struct scond scond_t;
|
||||
*/
|
||||
sthread_t *sthread_create(void (*thread_func)(void*), void *userdata);
|
||||
|
||||
/**
|
||||
* sthread_create_with_priority:
|
||||
* @start_routine : thread entry callback function
|
||||
* @userdata : pointer to userdata that will be made
|
||||
* available in thread entry callback function
|
||||
* @thread_priority : thread priority hint value from [1-100]
|
||||
*
|
||||
* Create a new thread. It is possible for the caller to give a hint
|
||||
* for the thread's priority from [1-100]. Any passed in @thread_priority
|
||||
* values that are outside of this range will cause sthread_create() to
|
||||
* create a new thread using the operating system's default thread
|
||||
* priority.
|
||||
*
|
||||
* Returns: pointer to new thread if successful, otherwise NULL.
|
||||
*/
|
||||
sthread_t *sthread_create_with_priority(void (*thread_func)(void*), void *userdata, int thread_priority);
|
||||
|
||||
/**
|
||||
* sthread_detach:
|
||||
* @thread : pointer to thread object
|
||||
@ -78,11 +99,6 @@ void sthread_join(sthread_t *thread);
|
||||
* sthread_isself:
|
||||
* @thread : pointer to thread object
|
||||
*
|
||||
* Join with a terminated thread. Waits for the thread specified by
|
||||
* @thread to terminate. If that thread has already terminated, then
|
||||
* it will return immediately. The thread specified by @thread must
|
||||
* be joinable.
|
||||
*
|
||||
* Returns: true (1) if calling thread is the specified thread
|
||||
*/
|
||||
bool sthread_isself(sthread_t *thread);
|
||||
@ -115,6 +131,15 @@ void slock_free(slock_t *lock);
|
||||
**/
|
||||
void slock_lock(slock_t *lock);
|
||||
|
||||
/**
|
||||
* slock_try_lock:
|
||||
* @lock : pointer to mutex object
|
||||
*
|
||||
* Attempts to lock a mutex. If a mutex is already locked by
|
||||
* another thread, return false. If the lock is acquired, return true.
|
||||
**/
|
||||
bool slock_try_lock(slock_t *lock);
|
||||
|
||||
/**
|
||||
* slock_unlock:
|
||||
* @lock : pointer to mutex object
|
||||
@ -183,6 +208,62 @@ int scond_broadcast(scond_t *cond);
|
||||
**/
|
||||
void scond_signal(scond_t *cond);
|
||||
|
||||
#ifdef HAVE_THREAD_STORAGE
|
||||
/**
|
||||
* @brief Creates a thread local storage key
|
||||
*
|
||||
* This function shall create thread-specific data key visible to all threads in
|
||||
* the process. The same key can be used by multiple threads to store
|
||||
* thread-local data.
|
||||
*
|
||||
* When the key is created NULL shall be associated with it in all active
|
||||
* threads. Whenever a new thread is spawned the all defined keys will be
|
||||
* associated with NULL on that thread.
|
||||
*
|
||||
* @param tls
|
||||
* @return whether the operation suceeded or not
|
||||
*/
|
||||
bool sthread_tls_create(sthread_tls_t *tls);
|
||||
|
||||
/**
|
||||
* @brief Deletes a thread local storage
|
||||
* @param tls
|
||||
* @return whether the operation suceeded or not
|
||||
*/
|
||||
bool sthread_tls_delete(sthread_tls_t *tls);
|
||||
|
||||
/**
|
||||
* @brief Retrieves thread specific data associated with a key
|
||||
*
|
||||
* There is no way to tell whether this function failed.
|
||||
*
|
||||
* @param tls
|
||||
* @return
|
||||
*/
|
||||
void *sthread_tls_get(sthread_tls_t *tls);
|
||||
|
||||
/**
|
||||
* @brief Binds thread specific data to a key
|
||||
* @param tls
|
||||
* @return Whether the operation suceeded or not
|
||||
*/
|
||||
bool sthread_tls_set(sthread_tls_t *tls, const void *data);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* @brief Get thread ID of specified thread
|
||||
* @param thread
|
||||
* @return The ID of the specified thread
|
||||
*/
|
||||
uintptr_t sthread_get_thread_id(sthread_t *thread);
|
||||
|
||||
/*
|
||||
* @brief Get thread ID of the current thread
|
||||
* @param
|
||||
* @return The ID of the current thread
|
||||
*/
|
||||
uintptr_t sthread_get_current_thread_id(void);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -59,7 +59,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length);
|
||||
* @bufsize : optional buffer size (-1 or 0 to use default)
|
||||
*
|
||||
* Opens a file for reading or writing, depending on the requested mode.
|
||||
* Returns a pointer to an RFILE if opened successfully, otherwise NULL.
|
||||
* @return A pointer to an RFILE if opened successfully, otherwise NULL.
|
||||
**/
|
||||
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints);
|
||||
|
||||
@ -75,16 +75,39 @@ void filestream_rewind(RFILE *stream);
|
||||
|
||||
int filestream_close(RFILE *stream);
|
||||
|
||||
/**
|
||||
* filestream_read_file:
|
||||
* @path : path to file.
|
||||
* @buf : buffer to allocate and read the contents of the
|
||||
* file into. Needs to be freed manually.
|
||||
* @len : optional output integer containing bytes read.
|
||||
*
|
||||
* Read the contents of a file into @buf.
|
||||
*
|
||||
* @return Non-zero on success.
|
||||
*/
|
||||
int64_t filestream_read_file(const char *path, void **buf, int64_t *len);
|
||||
|
||||
char* filestream_gets(RFILE *stream, char *s, size_t len);
|
||||
|
||||
int filestream_getc(RFILE *stream);
|
||||
|
||||
int filestream_vscanf(RFILE *stream, const char* format, va_list *args);
|
||||
|
||||
int filestream_scanf(RFILE *stream, const char* format, ...);
|
||||
|
||||
int filestream_eof(RFILE *stream);
|
||||
|
||||
/**
|
||||
* filestream_write_file:
|
||||
* @path : path to file.
|
||||
* @data : contents to write to the file.
|
||||
* @size : size of the contents.
|
||||
*
|
||||
* Writes data to a file.
|
||||
*
|
||||
* @return true on success, otherwise false.
|
||||
**/
|
||||
bool filestream_write_file(const char *path, const void *data, int64_t size);
|
||||
|
||||
int filestream_putc(RFILE *stream, int c);
|
||||
@ -105,7 +128,11 @@ const char* filestream_get_path(RFILE *stream);
|
||||
|
||||
bool filestream_exists(const char *path);
|
||||
|
||||
/* Returned pointer must be freed by the caller. */
|
||||
/**
|
||||
* filestream_getline:
|
||||
*
|
||||
* Returned pointer must be freed by the caller.
|
||||
**/
|
||||
char* filestream_getline(RFILE *stream);
|
||||
|
||||
libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream);
|
||||
|
@ -92,16 +92,20 @@ static INLINE bool string_ends_with_size(const char *str, const char *suffix,
|
||||
|
||||
static INLINE bool string_ends_with(const char *str, const char *suffix)
|
||||
{
|
||||
if (!str || !suffix)
|
||||
return false;
|
||||
return string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
|
||||
return str && suffix && string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
|
||||
}
|
||||
|
||||
/* Returns the length of 'str' (c.f. strlen()), but only
|
||||
/**
|
||||
* strlen_size:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return the length of 'str' (c.f. strlen()), but only
|
||||
* checks the first 'size' characters
|
||||
* - If 'str' is NULL, returns 0
|
||||
* - If 'str' is not NULL and no '\0' character is found
|
||||
* in the first 'size' characters, returns 'size' */
|
||||
* in the first 'size' characters, returns 'size'
|
||||
**/
|
||||
static INLINE size_t strlen_size(const char *str, size_t size)
|
||||
{
|
||||
size_t i = 0;
|
||||
@ -130,25 +134,68 @@ static INLINE bool string_is_equal_case_insensitive(const char *a,
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
static INLINE bool string_starts_with_case_insensitive(const char *str,
|
||||
const char *prefix)
|
||||
{
|
||||
int result = 0;
|
||||
const unsigned char *p1 = (const unsigned char*)str;
|
||||
const unsigned char *p2 = (const unsigned char*)prefix;
|
||||
|
||||
if (!str || !prefix)
|
||||
return false;
|
||||
if (p1 == p2)
|
||||
return true;
|
||||
|
||||
while ((result = tolower (*p1++) - tolower (*p2)) == 0)
|
||||
if (*p2++ == '\0')
|
||||
break;
|
||||
|
||||
return (result == 0 || *p2 == '\0');
|
||||
}
|
||||
|
||||
char *string_to_upper(char *s);
|
||||
|
||||
char *string_to_lower(char *s);
|
||||
|
||||
char *string_ucwords(char *s);
|
||||
|
||||
char *string_replace_substring(const char *in, const char *pattern,
|
||||
const char *by);
|
||||
char *string_replace_substring(const char *in,
|
||||
const char *pattern, size_t pattern_len,
|
||||
const char *replacement, size_t replacement_len);
|
||||
|
||||
/* Remove leading whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace_left:
|
||||
*
|
||||
* Remove leading whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace_left(char *const s);
|
||||
|
||||
/* Remove trailing whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace_right:
|
||||
*
|
||||
* Remove trailing whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace_right(char *const s);
|
||||
|
||||
/* Remove leading and trailing whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace:
|
||||
*
|
||||
* Remove leading and trailing whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace(char *const s);
|
||||
|
||||
/*
|
||||
/**
|
||||
* word_wrap:
|
||||
* @dst : pointer to destination buffer.
|
||||
* @dst_size : size of destination buffer.
|
||||
* @src : pointer to input string.
|
||||
* @src_len : length of @src
|
||||
* @line_width : max number of characters per line.
|
||||
* @wideglyph_width : not used, but is necessary to keep
|
||||
* compatibility with word_wrap_wideglyph().
|
||||
* @max_lines : max lines of destination string.
|
||||
* 0 means no limit.
|
||||
*
|
||||
* Wraps string specified by 'src' to destination buffer
|
||||
* specified by 'dst' and 'dst_size'.
|
||||
* This function assumes that all glyphs in the string
|
||||
@ -156,38 +203,18 @@ char *string_trim_whitespace(char *const s);
|
||||
* regular Latin characters - i.e. it will not wrap
|
||||
* correctly any text containing so-called 'wide' Unicode
|
||||
* characters (e.g. CJK languages, emojis, etc.).
|
||||
*
|
||||
* @param dst pointer to destination buffer.
|
||||
* @param dst_size size of destination buffer.
|
||||
* @param src pointer to input string.
|
||||
* @param line_width max number of characters per line.
|
||||
* @param wideglyph_width not used, but is necessary to keep
|
||||
* compatibility with word_wrap_wideglyph().
|
||||
* @param max_lines max lines of destination string.
|
||||
* 0 means no limit.
|
||||
*/
|
||||
void word_wrap(char *dst, size_t dst_size, const char *src,
|
||||
**/
|
||||
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);
|
||||
|
||||
/*
|
||||
* Wraps string specified by 'src' to destination buffer
|
||||
* specified by 'dst' and 'dst_size'.
|
||||
* This function assumes that all glyphs in the string
|
||||
* are:
|
||||
* - EITHER 'non-wide' Unicode glyphs, with an on-screen
|
||||
* pixel width similar to that of regular Latin characters
|
||||
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
|
||||
* with an on-screen pixel width defined by 'wideglyph_width'
|
||||
* Note that wrapping may occur in inappropriate locations
|
||||
* if 'src' string contains 'wide' Unicode characters whose
|
||||
* on-screen pixel width deviates greatly from the set
|
||||
* 'wideglyph_width' value.
|
||||
*
|
||||
* @param dst pointer to destination buffer.
|
||||
* @param dst_size size of destination buffer.
|
||||
* @param src pointer to input string.
|
||||
* @param line_width max number of characters per line.
|
||||
* @param wideglyph_width effective width of 'wide' Unicode glyphs.
|
||||
/**
|
||||
* word_wrap_wideglyph:
|
||||
* @dst : pointer to destination buffer.
|
||||
* @dst_size : size of destination buffer.
|
||||
* @src : pointer to input string.
|
||||
* @src_len : length of @src
|
||||
* @line_width : max number of characters per line.
|
||||
* @wideglyph_width : effective width of 'wide' Unicode glyphs.
|
||||
* the value here is normalised relative to the
|
||||
* typical on-screen pixel width of a regular
|
||||
* Latin character:
|
||||
@ -198,16 +225,35 @@ void word_wrap(char *dst, size_t dst_size, const char *src,
|
||||
* have an on-screen pixel width twice that of
|
||||
* regular Latin characters, wideglyph_width
|
||||
* would be 200
|
||||
* @param max_lines max lines of destination string.
|
||||
* @max_lines : max lines of destination string.
|
||||
* 0 means no limit.
|
||||
*/
|
||||
void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src,
|
||||
int line_width, int wideglyph_width, unsigned max_lines);
|
||||
*
|
||||
* Wraps string specified by @src to destination buffer
|
||||
* specified by @dst and @dst_size.
|
||||
* This function assumes that all glyphs in the string
|
||||
* are:
|
||||
* - EITHER 'non-wide' Unicode glyphs, with an on-screen
|
||||
* pixel width similar to that of regular Latin characters
|
||||
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
|
||||
* with an on-screen pixel width defined by @wideglyph_width
|
||||
* Note that wrapping may occur in inappropriate locations
|
||||
* if @src string contains 'wide' Unicode characters whose
|
||||
* on-screen pixel width deviates greatly from the set
|
||||
* @wideglyph_width value.
|
||||
**/
|
||||
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);
|
||||
|
||||
/* Splits string into tokens seperated by 'delim'
|
||||
/**
|
||||
* string_tokenize:
|
||||
*
|
||||
* Splits string into tokens seperated by @delim
|
||||
* > Returned token string must be free()'d
|
||||
* > Returns NULL if token is not found
|
||||
* > After each call, 'str' is set to the position after the
|
||||
* > After each call, @str is set to the position after the
|
||||
* last found token
|
||||
* > Tokens *include* empty strings
|
||||
* Usage example:
|
||||
@ -220,29 +266,120 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src,
|
||||
* free(token);
|
||||
* token = NULL;
|
||||
* }
|
||||
*/
|
||||
**/
|
||||
char* string_tokenize(char **str, const char *delim);
|
||||
|
||||
/* Removes every instance of character 'c' from 'str' */
|
||||
/**
|
||||
* string_remove_all_chars:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Removes every instance of character @c from @str
|
||||
**/
|
||||
void string_remove_all_chars(char *str, char c);
|
||||
|
||||
/* Replaces every instance of character 'find' in 'str'
|
||||
* with character 'replace' */
|
||||
/**
|
||||
* string_replace_all_chars:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
* @find : character to find
|
||||
* @replace : character to replace @find with
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strchr (in a loop)
|
||||
*
|
||||
* Replaces every instance of character @find in @str
|
||||
* with character @replace
|
||||
**/
|
||||
void string_replace_all_chars(char *str, char find, char replace);
|
||||
|
||||
/* Converts string to unsigned integer.
|
||||
* Returns 0 if string is invalid */
|
||||
/**
|
||||
* string_to_unsigned:
|
||||
* @str : input string
|
||||
*
|
||||
* Converts string to unsigned integer.
|
||||
*
|
||||
* @return 0 if string is invalid, otherwise > 0
|
||||
**/
|
||||
unsigned string_to_unsigned(const char *str);
|
||||
|
||||
/* Converts hexadecimal string to unsigned integer.
|
||||
/**
|
||||
* string_hex_to_unsigned:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
*
|
||||
* Converts hexadecimal string to unsigned integer.
|
||||
* Handles optional leading '0x'.
|
||||
* Returns 0 if string is invalid */
|
||||
*
|
||||
* @return 0 if string is invalid, otherwise > 0
|
||||
**/
|
||||
unsigned string_hex_to_unsigned(const char *str);
|
||||
|
||||
char *string_init(const char *src);
|
||||
|
||||
void string_set(char **string, const char *src);
|
||||
|
||||
/**
|
||||
* string_count_occurrences_single_character:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Get the total number of occurrences of character @c in @str.
|
||||
*
|
||||
* @return Total number of occurrences of character @c
|
||||
*/
|
||||
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.
|
||||
**/
|
||||
void string_replace_whitespace_with_single_character(char *str, char c);
|
||||
|
||||
/**
|
||||
* string_replace_multi_space_with_single_space:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Replaces multiple spaces with a single space in a string.
|
||||
**/
|
||||
void string_replace_multi_space_with_single_space(char *str);
|
||||
|
||||
/**
|
||||
* string_remove_all_whitespace:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Remove all spaces from the given string.
|
||||
**/
|
||||
void string_remove_all_whitespace(char *str_trimmed, const char *str);
|
||||
|
||||
/* Retrieve the last occurance of the given character in a string. */
|
||||
int string_index_last_occurance(const char *str, char c);
|
||||
|
||||
/**
|
||||
* string_find_index_substring_string:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
* @substr : substring to find in @str
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strstr
|
||||
*
|
||||
* Find the position of substring @substr in string @str.
|
||||
**/
|
||||
int string_find_index_substring_string(const char *str, const char *substr);
|
||||
|
||||
/**
|
||||
* string_copy_only_ascii:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Strips non-ASCII characters from a string.
|
||||
**/
|
||||
void string_copy_only_ascii(char *str_stripped, const char *str);
|
||||
|
||||
extern const unsigned char lr_char_props[256];
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
@ -62,7 +62,7 @@ enum vfs_scheme
|
||||
VFS_SCHEME_CDROM
|
||||
};
|
||||
|
||||
#ifndef __WINRT__
|
||||
#if !(defined(__WINRT__) && defined(__cplusplus_winrt))
|
||||
#ifdef VFS_FRONTEND
|
||||
struct retro_vfs_file_handle
|
||||
#else
|
||||
|
@ -71,6 +71,12 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *dirstream);
|
||||
|
||||
int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *dirstream);
|
||||
|
||||
#ifdef __WINRT__
|
||||
|
||||
void uwp_set_acl(const wchar_t* path, const wchar_t* AccessString);
|
||||
|
||||
#endif
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (gx_pthread.h).
|
||||
@ -84,10 +84,13 @@
|
||||
|
||||
typedef OSThread pthread_t;
|
||||
typedef mutex_t pthread_mutex_t;
|
||||
typedef OSCond pthread_cond_t;
|
||||
|
||||
#if defined(GX_PTHREAD_LEGACY)
|
||||
typedef void* pthread_mutexattr_t;
|
||||
typedef int pthread_attr_t;
|
||||
typedef OSCond pthread_cond_t;
|
||||
typedef OSCond pthread_condattr_t;
|
||||
#endif
|
||||
|
||||
static INLINE int pthread_create(pthread_t *thread,
|
||||
const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)
|
||||
@ -180,6 +183,4 @@ static INLINE int pthread_cond_destroy(pthread_cond_t *cond)
|
||||
return LWP_CondDestroy(*cond);
|
||||
}
|
||||
|
||||
extern int pthread_equal(pthread_t t1, pthread_t t2);
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (psp_pthread.h).
|
||||
@ -26,6 +26,7 @@
|
||||
|
||||
#ifdef VITA
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <pspkernel.h>
|
||||
#include <pspthreadman.h>
|
||||
@ -61,7 +62,6 @@ typedef struct
|
||||
sthreadEntry start_routine;
|
||||
} sthread_args_struct;
|
||||
|
||||
|
||||
static int psp_thread_wrap(SceSize args, void *argp)
|
||||
{
|
||||
sthread_args_struct* sthread_args = (sthread_args_struct*)argp;
|
||||
@ -72,7 +72,7 @@ static int psp_thread_wrap(SceSize args, void *argp)
|
||||
static INLINE int pthread_create(pthread_t *thread,
|
||||
const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)
|
||||
{
|
||||
sprintf(name_buffer, "0x%08X", (uint32_t) thread);
|
||||
snprintf(name_buffer, sizeof(name_buffer), "0x%08X", (unsigned int) thread);
|
||||
|
||||
#ifdef VITA
|
||||
*thread = sceKernelCreateThread(name_buffer, psp_thread_wrap,
|
||||
@ -92,11 +92,11 @@ static INLINE int pthread_create(pthread_t *thread,
|
||||
static INLINE int pthread_mutex_init(pthread_mutex_t *mutex,
|
||||
const pthread_mutexattr_t *attr)
|
||||
{
|
||||
sprintf(name_buffer, "0x%08X", (uint32_t) mutex);
|
||||
snprintf(name_buffer, sizeof(name_buffer), "0x%08X", (unsigned int) mutex);
|
||||
|
||||
#ifdef VITA
|
||||
*mutex = sceKernelCreateMutex(name_buffer, 0, 0, 0);
|
||||
if(*mutex<0)
|
||||
if (*mutex<0)
|
||||
return *mutex;
|
||||
return 0;
|
||||
#else
|
||||
@ -117,7 +117,6 @@ static INLINE int pthread_mutex_lock(pthread_mutex_t *mutex)
|
||||
{
|
||||
#ifdef VITA
|
||||
int ret = sceKernelLockMutex(*mutex, 1, 0);
|
||||
//sceClibPrintf("pthread_mutex_lock: %x\n",ret);
|
||||
return ret;
|
||||
|
||||
#else
|
||||
@ -130,7 +129,6 @@ static INLINE int pthread_mutex_unlock(pthread_mutex_t *mutex)
|
||||
{
|
||||
#ifdef VITA
|
||||
int ret = sceKernelUnlockMutex(*mutex, 1);
|
||||
//sceClibPrintf("pthread_mutex_unlock: %x\n",ret);
|
||||
return ret;
|
||||
#else
|
||||
/* FIXME: stub */
|
||||
@ -138,15 +136,12 @@ static INLINE int pthread_mutex_unlock(pthread_mutex_t *mutex)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static INLINE int pthread_join(pthread_t thread, void **retval)
|
||||
{
|
||||
|
||||
#ifdef VITA
|
||||
int res = sceKernelWaitThreadEnd(thread, 0, 0);
|
||||
if (res < 0) {
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
return sceKernelDeleteThread(thread);
|
||||
#else
|
||||
SceUInt timeout = (SceUInt)-1;
|
||||
@ -172,17 +167,15 @@ static INLINE int pthread_cond_wait(pthread_cond_t *cond,
|
||||
{
|
||||
#ifdef VITA
|
||||
int ret = pthread_mutex_lock(&cond->mutex);
|
||||
if (ret < 0) {
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
++cond->waiting;
|
||||
pthread_mutex_unlock(mutex);
|
||||
pthread_mutex_unlock(&cond->mutex);
|
||||
|
||||
ret = sceKernelWaitSema(cond->sema, 1, 0);
|
||||
if (ret < 0) {
|
||||
if (ret < 0)
|
||||
sceClibPrintf("Premature wakeup: %08X", ret);
|
||||
}
|
||||
pthread_mutex_lock(mutex);
|
||||
return ret;
|
||||
#else
|
||||
@ -197,9 +190,8 @@ static INLINE int pthread_cond_timedwait(pthread_cond_t *cond,
|
||||
{
|
||||
#ifdef VITA
|
||||
int ret = pthread_mutex_lock(&cond->mutex);
|
||||
if (ret < 0) {
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
++cond->waiting;
|
||||
pthread_mutex_unlock(mutex);
|
||||
pthread_mutex_unlock(&cond->mutex);
|
||||
@ -210,9 +202,8 @@ static INLINE int pthread_cond_timedwait(pthread_cond_t *cond,
|
||||
timeout += abstime->tv_nsec / 1.0e6;
|
||||
|
||||
ret = sceKernelWaitSema(cond->sema, 1, &timeout);
|
||||
if (ret < 0) {
|
||||
if (ret < 0)
|
||||
sceClibPrintf("Premature wakeup: %08X", ret);
|
||||
}
|
||||
pthread_mutex_lock(mutex);
|
||||
return ret;
|
||||
|
||||
@ -228,25 +219,23 @@ static INLINE int pthread_cond_init(pthread_cond_t *cond,
|
||||
#ifdef VITA
|
||||
|
||||
pthread_mutex_init(&cond->mutex,NULL);
|
||||
sceClibPrintf("pthread_cond_init: mutex %x\n",cond->mutex);
|
||||
if(cond->mutex<0){
|
||||
|
||||
if (cond->mutex<0)
|
||||
return cond->mutex;
|
||||
}
|
||||
sprintf(name_buffer, "0x%08X", (uint32_t) cond);
|
||||
//cond->sema = sceKernelCreateCond(name_buffer, 0, cond->mutex, 0);
|
||||
|
||||
snprintf(name_buffer, sizeof(name_buffer), "0x%08X", (unsigned int) cond);
|
||||
cond->sema = sceKernelCreateSema(name_buffer, 0, 0, 1, 0);
|
||||
sceClibPrintf("pthread_cond_init: sema %x\n",cond->sema);
|
||||
if(cond->sema<0){
|
||||
|
||||
if (cond->sema < 0)
|
||||
{
|
||||
pthread_mutex_destroy(&cond->mutex);
|
||||
return cond->sema;
|
||||
}
|
||||
|
||||
cond->waiting = 0;
|
||||
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
#else
|
||||
/* FIXME: stub */
|
||||
return 1;
|
||||
@ -257,10 +246,10 @@ static INLINE int pthread_cond_signal(pthread_cond_t *cond)
|
||||
{
|
||||
#ifdef VITA
|
||||
pthread_mutex_lock(&cond->mutex);
|
||||
if (cond->waiting) {
|
||||
if (cond->waiting)
|
||||
{
|
||||
--cond->waiting;
|
||||
int ret = sceKernelSignalSema(cond->sema, 1);
|
||||
sceClibPrintf("pthread_cond_signal: %x\n",ret);
|
||||
sceKernelSignalSema(cond->sema, 1);
|
||||
}
|
||||
pthread_mutex_unlock(&cond->mutex);
|
||||
return 0;
|
||||
@ -280,7 +269,7 @@ static INLINE int pthread_cond_destroy(pthread_cond_t *cond)
|
||||
{
|
||||
#ifdef VITA
|
||||
int ret = sceKernelDeleteSema(cond->sema);
|
||||
if(ret < 0)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sceKernelDeleteMutex(cond->mutex);
|
||||
@ -290,7 +279,6 @@ static INLINE int pthread_cond_destroy(pthread_cond_t *cond)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static INLINE int pthread_detach(pthread_t thread)
|
||||
{
|
||||
return 0;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rthreads.c).
|
||||
@ -21,10 +21,13 @@
|
||||
*/
|
||||
|
||||
#ifdef __unix__
|
||||
#ifndef __sun__
|
||||
#define _POSIX_C_SOURCE 199309
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <rthreads/rthreads.h>
|
||||
@ -37,21 +40,23 @@
|
||||
#include <xtl.h>
|
||||
#else
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0500 /*_WIN32_WINNT_WIN2K */
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
#elif defined(GEKKO)
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
#include "gx_pthread.h"
|
||||
#elif defined(PSP) || defined(VITA)
|
||||
#include "psp_pthread.h"
|
||||
#elif defined(__PS3__)
|
||||
#include <pthread.h>
|
||||
#include <sys/sys_time.h>
|
||||
#elif defined(_3DS)
|
||||
#include "ctr_pthread.h"
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#if defined(VITA)
|
||||
#if defined(VITA) || defined(BSD) || defined(ORBIS) || defined(__mips__) || defined(_3DS)
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
@ -70,6 +75,7 @@ struct sthread
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
HANDLE thread;
|
||||
DWORD id;
|
||||
#else
|
||||
pthread_t id;
|
||||
#endif
|
||||
@ -78,16 +84,49 @@ struct sthread
|
||||
struct slock
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
HANDLE lock;
|
||||
CRITICAL_SECTION lock;
|
||||
#else
|
||||
pthread_mutex_t lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
/* The syntax we'll use is mind-bending unless we use a struct. Plus, we might want to store more info later */
|
||||
/* This will be used as a linked list immplementing a queue of waiting threads */
|
||||
struct queue_entry
|
||||
{
|
||||
struct queue_entry *next;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct scond
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
/* With this implementation of scond, we don't have any way of waking
|
||||
* (or even identifying) specific threads
|
||||
* But we need to wake them in the order indicated by the queue.
|
||||
* This potato token will get get passed around every waiter.
|
||||
* The bearer can test whether he's next, and hold onto the potato if he is.
|
||||
* When he's done he can then put it back into play to progress
|
||||
* the queue further */
|
||||
HANDLE hot_potato;
|
||||
|
||||
/* The primary signalled event. Hot potatoes are passed until this is set. */
|
||||
HANDLE event;
|
||||
|
||||
/* the head of the queue; NULL if queue is empty */
|
||||
struct queue_entry *head;
|
||||
|
||||
/* equivalent to the queue length */
|
||||
int waiters;
|
||||
|
||||
/* how many waiters in the queue have been conceptually wakened by signals
|
||||
* (even if we haven't managed to actually wake them yet) */
|
||||
int wakens;
|
||||
|
||||
/* used to control access to this scond, in case the user fails */
|
||||
CRITICAL_SECTION cs;
|
||||
|
||||
#else
|
||||
pthread_cond_t cond;
|
||||
#endif
|
||||
@ -119,34 +158,95 @@ static void *thread_wrap(void *data_)
|
||||
*/
|
||||
sthread_t *sthread_create(void (*thread_func)(void*), void *userdata)
|
||||
{
|
||||
return sthread_create_with_priority(thread_func, userdata, 0);
|
||||
}
|
||||
|
||||
/* TODO/FIXME - this needs to be implemented for Switch/3DS */
|
||||
#if !defined(SWITCH) && !defined(USE_WIN32_THREADS) && !defined(_3DS) && !defined(GEKKO) && !defined(__HAIKU__) && !defined(EMSCRIPTEN)
|
||||
#define HAVE_THREAD_ATTR
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sthread_create_with_priority:
|
||||
* @start_routine : thread entry callback function
|
||||
* @userdata : pointer to userdata that will be made
|
||||
* available in thread entry callback function
|
||||
* @thread_priority : thread priority hint value from [1-100]
|
||||
*
|
||||
* Create a new thread. It is possible for the caller to give a hint
|
||||
* for the thread's priority from [1-100]. Any passed in @thread_priority
|
||||
* values that are outside of this range will cause sthread_create() to
|
||||
* create a new thread using the operating system's default thread
|
||||
* priority.
|
||||
*
|
||||
* Returns: pointer to new thread if successful, otherwise NULL.
|
||||
*/
|
||||
sthread_t *sthread_create_with_priority(void (*thread_func)(void*), void *userdata, int thread_priority)
|
||||
{
|
||||
#ifdef HAVE_THREAD_ATTR
|
||||
pthread_attr_t thread_attr;
|
||||
bool thread_attr_needed = false;
|
||||
#endif
|
||||
bool thread_created = false;
|
||||
struct thread_data *data = NULL;
|
||||
sthread_t *thread = (sthread_t*)calloc(1, sizeof(*thread));
|
||||
sthread_t *thread = (sthread_t*)malloc(sizeof(*thread));
|
||||
|
||||
if (!thread)
|
||||
return NULL;
|
||||
|
||||
data = (struct thread_data*)calloc(1, sizeof(*data));
|
||||
if (!data)
|
||||
goto error;
|
||||
if (!(data = (struct thread_data*)malloc(sizeof(*data))))
|
||||
{
|
||||
free(thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->func = thread_func;
|
||||
data->userdata = userdata;
|
||||
|
||||
thread->id = 0;
|
||||
#ifdef USE_WIN32_THREADS
|
||||
thread->thread = CreateThread(NULL, 0, thread_wrap, data, 0, NULL);
|
||||
thread->thread = CreateThread(NULL, 0, thread_wrap,
|
||||
data, 0, &thread->id);
|
||||
thread_created = !!thread->thread;
|
||||
#else
|
||||
#ifdef HAVE_THREAD_ATTR
|
||||
pthread_attr_init(&thread_attr);
|
||||
|
||||
if ((thread_priority >= 1) && (thread_priority <= 100))
|
||||
{
|
||||
struct sched_param sp;
|
||||
memset(&sp, 0, sizeof(struct sched_param));
|
||||
sp.sched_priority = thread_priority;
|
||||
pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);
|
||||
pthread_attr_setschedparam(&thread_attr, &sp);
|
||||
|
||||
thread_attr_needed = true;
|
||||
}
|
||||
|
||||
#if defined(VITA)
|
||||
pthread_attr_setstacksize(&thread_attr , 0x10000 );
|
||||
thread_attr_needed = true;
|
||||
#elif defined(__APPLE__)
|
||||
/* Default stack size on Apple is 512Kb;
|
||||
* for PS2 disc scanning and other reasons, we'd like 2MB. */
|
||||
pthread_attr_setstacksize(&thread_attr , 0x200000 );
|
||||
thread_attr_needed = true;
|
||||
#endif
|
||||
|
||||
if (thread_attr_needed)
|
||||
thread_created = pthread_create(&thread->id, &thread_attr, thread_wrap, data) == 0;
|
||||
else
|
||||
thread_created = pthread_create(&thread->id, NULL, thread_wrap, data) == 0;
|
||||
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
#else
|
||||
thread_created = pthread_create(&thread->id, NULL, thread_wrap, data) == 0;
|
||||
#endif
|
||||
|
||||
if (!thread_created)
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
if (thread_created)
|
||||
return thread;
|
||||
|
||||
error:
|
||||
if (data)
|
||||
free(data);
|
||||
free(thread);
|
||||
return NULL;
|
||||
@ -157,7 +257,7 @@ error:
|
||||
* @thread : pointer to thread object
|
||||
*
|
||||
* Detach a thread. When a detached thread terminates, its
|
||||
* resource sare automatically released back to the system
|
||||
* resources are automatically released back to the system
|
||||
* without the need for another thread to join with the
|
||||
* terminated thread.
|
||||
*
|
||||
@ -170,7 +270,9 @@ int sthread_detach(sthread_t *thread)
|
||||
free(thread);
|
||||
return 0;
|
||||
#else
|
||||
return pthread_detach(thread->id);
|
||||
int ret = pthread_detach(thread->id);
|
||||
free(thread);
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -187,6 +289,8 @@ int sthread_detach(sthread_t *thread)
|
||||
*/
|
||||
void sthread_join(sthread_t *thread)
|
||||
{
|
||||
if (!thread)
|
||||
return;
|
||||
#ifdef USE_WIN32_THREADS
|
||||
WaitForSingleObject(thread->thread, INFINITE);
|
||||
CloseHandle(thread->thread);
|
||||
@ -196,25 +300,22 @@ void sthread_join(sthread_t *thread)
|
||||
free(thread);
|
||||
}
|
||||
|
||||
#if !defined(GEKKO)
|
||||
/**
|
||||
* sthread_isself:
|
||||
* @thread : pointer to thread object
|
||||
*
|
||||
* Join with a terminated thread. Waits for the thread specified by
|
||||
* @thread to terminate. If that thread has already terminated, then
|
||||
* it will return immediately. The thread specified by @thread must
|
||||
* be joinable.
|
||||
*
|
||||
* Returns: true (1) if calling thread is the specified thread
|
||||
*/
|
||||
bool sthread_isself(sthread_t *thread)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return GetCurrentThread() == thread->thread;
|
||||
return thread ? GetCurrentThreadId() == thread->id : false;
|
||||
#else
|
||||
return pthread_equal(pthread_self(),thread->id);
|
||||
return thread ? pthread_equal(pthread_self(), thread->id) : false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* slock_new:
|
||||
@ -229,21 +330,16 @@ slock_t *slock_new(void)
|
||||
slock_t *lock = (slock_t*)calloc(1, sizeof(*lock));
|
||||
if (!lock)
|
||||
return NULL;
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
lock->lock = CreateMutex(NULL, FALSE, NULL);
|
||||
if (!lock->lock)
|
||||
goto error;
|
||||
InitializeCriticalSection(&lock->lock);
|
||||
#else
|
||||
if ((pthread_mutex_init(&lock->lock, NULL) < 0))
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
return lock;
|
||||
|
||||
error:
|
||||
slock_free(lock);
|
||||
if (pthread_mutex_init(&lock->lock, NULL) != 0)
|
||||
{
|
||||
free(lock);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
return lock;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,7 +354,7 @@ void slock_free(slock_t *lock)
|
||||
return;
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
CloseHandle(lock->lock);
|
||||
DeleteCriticalSection(&lock->lock);
|
||||
#else
|
||||
pthread_mutex_destroy(&lock->lock);
|
||||
#endif
|
||||
@ -275,13 +371,31 @@ void slock_free(slock_t *lock)
|
||||
**/
|
||||
void slock_lock(slock_t *lock)
|
||||
{
|
||||
if (!lock)
|
||||
return;
|
||||
#ifdef USE_WIN32_THREADS
|
||||
WaitForSingleObject(lock->lock, INFINITE);
|
||||
EnterCriticalSection(&lock->lock);
|
||||
#else
|
||||
pthread_mutex_lock(&lock->lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* slock_try_lock:
|
||||
* @lock : pointer to mutex object
|
||||
*
|
||||
* Attempts to lock a mutex. If a mutex is already locked by
|
||||
* another thread, return false. If the lock is acquired, return true.
|
||||
**/
|
||||
bool slock_try_lock(slock_t *lock)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return lock && TryEnterCriticalSection(&lock->lock);
|
||||
#else
|
||||
return lock && (pthread_mutex_trylock(&lock->lock) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* slock_unlock:
|
||||
* @lock : pointer to mutex object
|
||||
@ -290,8 +404,10 @@ void slock_lock(slock_t *lock)
|
||||
**/
|
||||
void slock_unlock(slock_t *lock)
|
||||
{
|
||||
if (!lock)
|
||||
return;
|
||||
#ifdef USE_WIN32_THREADS
|
||||
ReleaseMutex(lock->lock);
|
||||
LeaveCriticalSection(&lock->lock);
|
||||
#else
|
||||
pthread_mutex_unlock(&lock->lock);
|
||||
#endif
|
||||
@ -308,21 +424,50 @@ void slock_unlock(slock_t *lock)
|
||||
**/
|
||||
scond_t *scond_new(void)
|
||||
{
|
||||
bool event_created = false;
|
||||
scond_t *cond = (scond_t*)calloc(1, sizeof(*cond));
|
||||
|
||||
if (!cond)
|
||||
return NULL;
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
cond->event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
event_created = !!cond->event;
|
||||
#else
|
||||
event_created = (pthread_cond_init(&cond->cond, NULL) == 0);
|
||||
#endif
|
||||
|
||||
if (!event_created)
|
||||
/* This is very complex because recreating condition variable semantics
|
||||
* with Win32 parts is not easy.
|
||||
*
|
||||
* The main problem is that a condition variable can't be used to
|
||||
* "pre-wake" a thread (it will get wakened only after it's waited).
|
||||
*
|
||||
* Whereas a win32 event can pre-wake a thread (the event will be set
|
||||
* in advance, so a 'waiter' won't even have to wait on it).
|
||||
*
|
||||
* Keep in mind a condition variable can apparently pre-wake a thread,
|
||||
* insofar as spurious wakeups are always possible,
|
||||
* but nobody will be expecting this and it does not need to be simulated.
|
||||
*
|
||||
* Moreover, we won't be doing this, because it counts as a spurious wakeup
|
||||
* -- someone else with a genuine claim must get wakened, in any case.
|
||||
*
|
||||
* Therefore we choose to wake only one of the correct waiting threads.
|
||||
* So at the very least, we need to do something clever. But there's
|
||||
* bigger problems.
|
||||
* We don't even have a straightforward way in win32 to satisfy
|
||||
* pthread_cond_wait's atomicity requirement. The bulk of this
|
||||
* algorithm is solving that.
|
||||
*
|
||||
* Note: We might could simplify this using vista+ condition variables,
|
||||
* but we wanted an XP compatible solution. */
|
||||
if (!(cond->event = CreateEvent(NULL, FALSE, FALSE, NULL)))
|
||||
goto error;
|
||||
if (!(cond->hot_potato = CreateEvent(NULL, FALSE, FALSE, NULL)))
|
||||
{
|
||||
CloseHandle(cond->event);
|
||||
goto error;
|
||||
}
|
||||
|
||||
InitializeCriticalSection(&cond->cs);
|
||||
#else
|
||||
if (pthread_cond_init(&cond->cond, NULL) != 0)
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
return cond;
|
||||
|
||||
@ -344,12 +489,220 @@ void scond_free(scond_t *cond)
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
CloseHandle(cond->event);
|
||||
CloseHandle(cond->hot_potato);
|
||||
DeleteCriticalSection(&cond->cs);
|
||||
#else
|
||||
pthread_cond_destroy(&cond->cond);
|
||||
#endif
|
||||
free(cond);
|
||||
}
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds)
|
||||
{
|
||||
struct queue_entry myentry;
|
||||
struct queue_entry **ptr;
|
||||
|
||||
#if _WIN32_WINNT >= 0x0500 || defined(_XBOX)
|
||||
static LARGE_INTEGER performanceCounterFrequency;
|
||||
LARGE_INTEGER tsBegin;
|
||||
static bool first_init = true;
|
||||
#else
|
||||
static bool beginPeriod = false;
|
||||
DWORD tsBegin;
|
||||
#endif
|
||||
DWORD waitResult;
|
||||
DWORD dwFinalTimeout = dwMilliseconds; /* Careful! in case we begin in the head,
|
||||
we don't do the hot potato stuff,
|
||||
so this timeout needs presetting. */
|
||||
|
||||
/* Reminder: `lock` is held before this is called. */
|
||||
/* however, someone else may have called scond_signal without the lock. soo... */
|
||||
EnterCriticalSection(&cond->cs);
|
||||
|
||||
/* since this library is meant for realtime game software
|
||||
* I have no problem setting this to 1 and forgetting about it. */
|
||||
#if _WIN32_WINNT >= 0x0500 || defined(_XBOX)
|
||||
if (first_init)
|
||||
{
|
||||
performanceCounterFrequency.QuadPart = 0;
|
||||
first_init = false;
|
||||
}
|
||||
|
||||
if (performanceCounterFrequency.QuadPart == 0)
|
||||
QueryPerformanceFrequency(&performanceCounterFrequency);
|
||||
#else
|
||||
if (!beginPeriod)
|
||||
{
|
||||
beginPeriod = true;
|
||||
timeBeginPeriod(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Now we can take a good timestamp for use in faking the timeout ourselves. */
|
||||
/* But don't bother unless we need to (to save a little time) */
|
||||
if (dwMilliseconds != INFINITE)
|
||||
#if _WIN32_WINNT >= 0x0500 || defined(_XBOX)
|
||||
QueryPerformanceCounter(&tsBegin);
|
||||
#else
|
||||
tsBegin = timeGetTime();
|
||||
#endif
|
||||
|
||||
/* add ourselves to a queue of waiting threads */
|
||||
ptr = &cond->head;
|
||||
|
||||
/* walk to the end of the linked list */
|
||||
while (*ptr)
|
||||
ptr = &((*ptr)->next);
|
||||
|
||||
*ptr = &myentry;
|
||||
myentry.next = NULL;
|
||||
|
||||
cond->waiters++;
|
||||
|
||||
/* now the conceptual lock release and condition block are supposed to be atomic.
|
||||
* we can't do that in Windows, but we can simulate the effects by using
|
||||
* the queue, by the following analysis:
|
||||
* What happens if they aren't atomic?
|
||||
*
|
||||
* 1. a signaller can rush in and signal, expecting a waiter to get it;
|
||||
* but the waiter wouldn't, because he isn't blocked yet.
|
||||
* Solution: Win32 events make this easy. The event will sit there enabled
|
||||
*
|
||||
* 2. a signaller can rush in and signal, and then turn right around and wait.
|
||||
* Solution: the signaller will get queued behind the waiter, who's
|
||||
* enqueued before he releases the mutex. */
|
||||
|
||||
/* It's my turn if I'm the head of the queue.
|
||||
* Check to see if it's my turn. */
|
||||
while (cond->head != &myentry)
|
||||
{
|
||||
/* It isn't my turn: */
|
||||
DWORD timeout = INFINITE;
|
||||
|
||||
/* As long as someone is even going to be able to wake up
|
||||
* when they receive the potato, keep it going round. */
|
||||
if (cond->wakens > 0)
|
||||
SetEvent(cond->hot_potato);
|
||||
|
||||
/* Assess the remaining timeout time */
|
||||
if (dwMilliseconds != INFINITE)
|
||||
{
|
||||
#if _WIN32_WINNT >= 0x0500 || defined(_XBOX)
|
||||
LARGE_INTEGER now;
|
||||
LONGLONG elapsed;
|
||||
|
||||
QueryPerformanceCounter(&now);
|
||||
elapsed = now.QuadPart - tsBegin.QuadPart;
|
||||
elapsed *= 1000;
|
||||
elapsed /= performanceCounterFrequency.QuadPart;
|
||||
#else
|
||||
DWORD now = timeGetTime();
|
||||
DWORD elapsed = now - tsBegin;
|
||||
#endif
|
||||
|
||||
/* Try one last time with a zero timeout (keeps the code simpler) */
|
||||
if (elapsed > dwMilliseconds)
|
||||
elapsed = dwMilliseconds;
|
||||
|
||||
timeout = dwMilliseconds - elapsed;
|
||||
}
|
||||
|
||||
/* Let someone else go */
|
||||
LeaveCriticalSection(&lock->lock);
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
|
||||
/* Wait a while to catch the hot potato..
|
||||
* someone else should get a chance to go */
|
||||
/* After all, it isn't my turn (and it must be someone else's) */
|
||||
Sleep(0);
|
||||
waitResult = WaitForSingleObject(cond->hot_potato, timeout);
|
||||
|
||||
/* I should come out of here with the main lock taken */
|
||||
EnterCriticalSection(&lock->lock);
|
||||
EnterCriticalSection(&cond->cs);
|
||||
|
||||
if (waitResult == WAIT_TIMEOUT)
|
||||
{
|
||||
/* Out of time! Now, let's think about this. I do have the potato now--
|
||||
* maybe it's my turn, and I have the event?
|
||||
* If that's the case, I could proceed right now without aborting
|
||||
* due to timeout.
|
||||
*
|
||||
* However.. I DID wait a real long time. The caller was willing
|
||||
* to wait that long.
|
||||
*
|
||||
* I choose to give him one last chance with a zero timeout
|
||||
* in the next step
|
||||
*/
|
||||
if (cond->head == &myentry)
|
||||
{
|
||||
dwFinalTimeout = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's not our turn and we're out of time. Give up.
|
||||
* Remove ourself from the queue and bail. */
|
||||
struct queue_entry *curr = cond->head;
|
||||
|
||||
while (curr->next != &myentry)
|
||||
curr = curr->next;
|
||||
curr->next = myentry.next;
|
||||
cond->waiters--;
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* It's my turn now -- and I hold the potato */
|
||||
|
||||
/* I still have the main lock, in any case */
|
||||
/* I need to release it so that someone can set the event */
|
||||
LeaveCriticalSection(&lock->lock);
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
|
||||
/* Wait for someone to actually signal this condition */
|
||||
/* We're the only waiter waiting on the event right now -- everyone else
|
||||
* is waiting on something different */
|
||||
waitResult = WaitForSingleObject(cond->event, dwFinalTimeout);
|
||||
|
||||
/* Take the main lock so we can do work. Nobody else waits on this lock
|
||||
* for very long, so even though it's GO TIME we won't have to wait long */
|
||||
EnterCriticalSection(&lock->lock);
|
||||
EnterCriticalSection(&cond->cs);
|
||||
|
||||
/* Remove ourselves from the queue */
|
||||
cond->head = myentry.next;
|
||||
cond->waiters--;
|
||||
|
||||
if (waitResult == WAIT_TIMEOUT)
|
||||
{
|
||||
/* Oops! ran out of time in the final wait. Just bail. */
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If any other wakenings are pending, go ahead and set it up */
|
||||
/* There may actually be no waiters. That's OK. The first waiter will come in,
|
||||
* find it's his turn, and immediately get the signaled event */
|
||||
cond->wakens--;
|
||||
if (cond->wakens > 0)
|
||||
{
|
||||
SetEvent(cond->event);
|
||||
|
||||
/* Progress the queue: Put the hot potato back into play. It'll be
|
||||
* tossed around until next in line gets it */
|
||||
SetEvent(cond->hot_potato);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* scond_wait:
|
||||
* @cond : pointer to condition variable object
|
||||
@ -360,10 +713,7 @@ void scond_free(scond_t *cond)
|
||||
void scond_wait(scond_t *cond, slock_t *lock)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
WaitForSingleObject(cond->event, 0);
|
||||
|
||||
SignalObjectAndWait(lock->lock, cond->event, INFINITE, FALSE);
|
||||
slock_lock(lock);
|
||||
_scond_wait_win32(cond, lock, INFINITE);
|
||||
#else
|
||||
pthread_cond_wait(&cond->cond, &lock->lock);
|
||||
#endif
|
||||
@ -379,9 +729,17 @@ void scond_wait(scond_t *cond, slock_t *lock)
|
||||
int scond_broadcast(scond_t *cond)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
/* FIXME _- check how this function should differ
|
||||
* from scond_signal implementation. */
|
||||
/* Remember, we currently have mutex */
|
||||
if (cond->waiters != 0)
|
||||
{
|
||||
/* Awaken everything which is currently queued up */
|
||||
if (cond->wakens == 0)
|
||||
SetEvent(cond->event);
|
||||
cond->wakens = cond->waiters;
|
||||
|
||||
/* Since there is now at least one pending waken, the potato must be in play */
|
||||
SetEvent(cond->hot_potato);
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return pthread_cond_broadcast(&cond->cond);
|
||||
@ -398,7 +756,37 @@ int scond_broadcast(scond_t *cond)
|
||||
void scond_signal(scond_t *cond)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
|
||||
/* Unfortunately, pthread_cond_signal does not require that the
|
||||
* lock be held in advance */
|
||||
/* To avoid stomping on the condvar from other threads, we need
|
||||
* to control access to it with this */
|
||||
EnterCriticalSection(&cond->cs);
|
||||
|
||||
/* remember: we currently have mutex */
|
||||
if (cond->waiters == 0)
|
||||
{
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
return;
|
||||
}
|
||||
|
||||
/* wake up the next thing in the queue */
|
||||
if (cond->wakens == 0)
|
||||
SetEvent(cond->event);
|
||||
|
||||
cond->wakens++;
|
||||
|
||||
/* The data structure is done being modified.. I think we can leave the CS now.
|
||||
* This would prevent some other thread from receiving the hot potato and then
|
||||
* immediately stalling for the critical section.
|
||||
* But remember, we were trying to replicate a semantic where this entire
|
||||
* scond_signal call was controlled (by the user) by a lock.
|
||||
* So in case there's trouble with this, we can move it after SetEvent() */
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
|
||||
/* Since there is now at least one pending waken, the potato must be in play */
|
||||
SetEvent(cond->hot_potato);
|
||||
|
||||
#else
|
||||
pthread_cond_signal(&cond->cond);
|
||||
#endif
|
||||
@ -419,46 +807,60 @@ void scond_signal(scond_t *cond)
|
||||
bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
DWORD ret;
|
||||
|
||||
WaitForSingleObject(cond->event, 0);
|
||||
ret = SignalObjectAndWait(lock->lock, cond->event,
|
||||
(DWORD)(timeout_us) / 1000, FALSE);
|
||||
|
||||
slock_lock(lock);
|
||||
return ret == WAIT_OBJECT_0;
|
||||
/* How to convert a microsecond (us) timeout to millisecond (ms)?
|
||||
*
|
||||
* Someone asking for a 0 timeout clearly wants immediate timeout.
|
||||
* Someone asking for a 1 timeout clearly wants an actual timeout
|
||||
* of the minimum length */
|
||||
/* The implementation of a 0 timeout here with pthreads is sketchy.
|
||||
* It isn't clear what happens if pthread_cond_timedwait is called with NOW.
|
||||
* Moreover, it is possible that this thread gets pre-empted after the
|
||||
* clock_gettime but before the pthread_cond_timedwait.
|
||||
* In order to help smoke out problems caused by this strange usage,
|
||||
* let's treat a 0 timeout as always timing out.
|
||||
*/
|
||||
if (timeout_us == 0)
|
||||
return false;
|
||||
else if (timeout_us < 1000)
|
||||
return _scond_wait_win32(cond, lock, 1);
|
||||
/* Someone asking for 1000 or 1001 timeout shouldn't
|
||||
* accidentally get 2ms. */
|
||||
return _scond_wait_win32(cond, lock, timeout_us / 1000);
|
||||
#else
|
||||
int ret;
|
||||
int64_t seconds, remainder;
|
||||
struct timespec now = {0};
|
||||
|
||||
struct timespec now;
|
||||
#ifdef __MACH__
|
||||
/* OSX doesn't have clock_gettime. */
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts;
|
||||
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||
clock_get_time(cclock, &mts);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
now.tv_sec = mts.tv_sec;
|
||||
now.tv_nsec = mts.tv_nsec;
|
||||
#elif defined(__PS3__)
|
||||
#elif !defined(__PSL1GHT__) && defined(__PS3__)
|
||||
sys_time_sec_t s;
|
||||
sys_time_nsec_t n;
|
||||
|
||||
sys_time_get_current_time(&s, &n);
|
||||
now.tv_sec = s;
|
||||
now.tv_nsec = n;
|
||||
#elif defined(__mips__) || defined(VITA)
|
||||
#elif defined(PS2)
|
||||
int tickms = ps2_clock();
|
||||
now.tv_sec = tickms / 1000;
|
||||
now.tv_nsec = tickms * 1000;
|
||||
#elif !defined(DINGUX_BETA) && (defined(__mips__) || defined(VITA) || defined(_3DS))
|
||||
struct timeval tm;
|
||||
|
||||
gettimeofday(&tm, NULL);
|
||||
now.tv_sec = tm.tv_sec;
|
||||
now.tv_nsec = tm.tv_usec * 1000;
|
||||
#elif defined(RETRO_WIN32_USE_PTHREADS)
|
||||
_ftime64_s(&now);
|
||||
#elif !defined(GEKKO)
|
||||
/* timeout on libogc is duration, not end time. */
|
||||
#elif defined(GEKKO)
|
||||
/* Avoid gettimeofday due to it being reported to be broken */
|
||||
const uint64_t tickms = gettime() / TB_TIMER_CLOCK;
|
||||
now.tv_sec = tickms / 1000;
|
||||
now.tv_nsec = tickms * 1000;
|
||||
#else
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
#endif
|
||||
|
||||
@ -468,7 +870,66 @@ bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us)
|
||||
now.tv_sec += seconds;
|
||||
now.tv_nsec += remainder * INT64_C(1000);
|
||||
|
||||
ret = pthread_cond_timedwait(&cond->cond, &lock->lock, &now);
|
||||
return (ret == 0);
|
||||
if (now.tv_nsec > 1000000000)
|
||||
{
|
||||
now.tv_nsec -= 1000000000;
|
||||
now.tv_sec += 1;
|
||||
}
|
||||
|
||||
return (pthread_cond_timedwait(&cond->cond, &lock->lock, &now) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_THREAD_STORAGE
|
||||
bool sthread_tls_create(sthread_tls_t *tls)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return (*tls = TlsAlloc()) != TLS_OUT_OF_INDEXES;
|
||||
#else
|
||||
return pthread_key_create((pthread_key_t*)tls, NULL) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool sthread_tls_delete(sthread_tls_t *tls)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return TlsFree(*tls) != 0;
|
||||
#else
|
||||
return pthread_key_delete(*tls) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *sthread_tls_get(sthread_tls_t *tls)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return TlsGetValue(*tls);
|
||||
#else
|
||||
return pthread_getspecific(*tls);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool sthread_tls_set(sthread_tls_t *tls, const void *data)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return TlsSetValue(*tls, (void*)data) != 0;
|
||||
#else
|
||||
return pthread_setspecific(*tls, data) == 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
uintptr_t sthread_get_thread_id(sthread_t *thread)
|
||||
{
|
||||
if (thread)
|
||||
return (uintptr_t)thread->id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t sthread_get_current_thread_id(void)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return (uintptr_t)GetCurrentThreadId();
|
||||
#else
|
||||
return (uintptr_t)pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2016 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (xenon_sdl_threads.c).
|
||||
@ -20,7 +20,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// libSDLxenon doesn't implement this yet :[. Implement it very stupidly for now. ;)
|
||||
/* libSDLxenon doesn't implement this yet :[. Implement it very stupidly for now. ;) */
|
||||
|
||||
#include "SDL_thread.h"
|
||||
#include "SDL_mutex.h"
|
||||
@ -56,4 +56,3 @@ int SDL_CondSignal(SDL_cond *cond)
|
||||
*(volatile bool*)cond = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
@ -46,7 +45,6 @@ struct RFILE
|
||||
{
|
||||
struct retro_vfs_file_handle *hfile;
|
||||
bool error_flag;
|
||||
bool eof_flag;
|
||||
};
|
||||
|
||||
static retro_vfs_get_path_t filestream_get_path_cb = NULL;
|
||||
@ -109,17 +107,13 @@ bool filestream_exists(const char *path)
|
||||
|
||||
if (!path || !*path)
|
||||
return false;
|
||||
|
||||
dummy = filestream_open(
|
||||
if (!(dummy = filestream_open(
|
||||
path,
|
||||
RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
if (!dummy)
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
return false;
|
||||
|
||||
if (filestream_close(dummy) != 0)
|
||||
if (dummy)
|
||||
free(dummy);
|
||||
|
||||
dummy = NULL;
|
||||
@ -165,7 +159,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length)
|
||||
* @hints :
|
||||
*
|
||||
* Opens a file for reading or writing, depending on the requested mode.
|
||||
* Returns a pointer to an RFILE if opened successfully, otherwise NULL.
|
||||
* @return A pointer to an RFILE if opened successfully, otherwise NULL.
|
||||
**/
|
||||
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
|
||||
{
|
||||
@ -184,7 +178,6 @@ RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
|
||||
|
||||
output = (RFILE*)malloc(sizeof(RFILE));
|
||||
output->error_flag = false;
|
||||
output->eof_flag = false;
|
||||
output->hfile = fp;
|
||||
return output;
|
||||
}
|
||||
@ -221,12 +214,12 @@ int filestream_getc(RFILE *stream)
|
||||
return EOF;
|
||||
}
|
||||
|
||||
int filestream_scanf(RFILE *stream, const char* format, ...)
|
||||
int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
|
||||
{
|
||||
char buf[4096];
|
||||
char subfmt[64];
|
||||
va_list args;
|
||||
const char * bufiter = buf;
|
||||
va_list args_copy;
|
||||
const char *bufiter = buf;
|
||||
int ret = 0;
|
||||
int64_t startpos = filestream_tell(stream);
|
||||
int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1);
|
||||
@ -236,7 +229,16 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
|
||||
|
||||
buf[maxlen] = '\0';
|
||||
|
||||
va_start(args, format);
|
||||
/* Have to copy the input va_list here
|
||||
* > Calling va_arg() on 'args' directly would
|
||||
* cause the va_list to have an indeterminate value
|
||||
* in the function calling filestream_vscanf(),
|
||||
* leading to unexpected behaviour */
|
||||
#ifdef __va_copy
|
||||
__va_copy(args_copy, *args);
|
||||
#else
|
||||
va_copy(args_copy, *args);
|
||||
#endif
|
||||
|
||||
while (*format)
|
||||
{
|
||||
@ -302,7 +304,7 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
|
||||
}
|
||||
else
|
||||
{
|
||||
int v = sscanf(bufiter, subfmt, va_arg(args, void*), &sublen);
|
||||
int v = sscanf(bufiter, subfmt, va_arg(args_copy, void*), &sublen);
|
||||
if (v == EOF)
|
||||
return EOF;
|
||||
if (v != 1)
|
||||
@ -327,13 +329,23 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
filestream_seek(stream, startpos+(bufiter-buf),
|
||||
va_end(args_copy);
|
||||
filestream_seek(stream, startpos + (bufiter - buf),
|
||||
RETRO_VFS_SEEK_POSITION_START);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int filestream_scanf(RFILE *stream, const char* format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list vl;
|
||||
va_start(vl, format);
|
||||
result = filestream_vscanf(stream, format, &vl);
|
||||
va_end(vl);
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
|
||||
{
|
||||
int64_t output;
|
||||
@ -348,14 +360,12 @@ int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
|
||||
if (output == VFS_ERROR_RETURN_VALUE)
|
||||
stream->error_flag = true;
|
||||
|
||||
stream->eof_flag = false;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
int filestream_eof(RFILE *stream)
|
||||
{
|
||||
return stream->eof_flag;
|
||||
return filestream_tell(stream) == filestream_get_size(stream) ? EOF : 0;
|
||||
}
|
||||
|
||||
int64_t filestream_tell(RFILE *stream)
|
||||
@ -380,7 +390,6 @@ void filestream_rewind(RFILE *stream)
|
||||
return;
|
||||
filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START);
|
||||
stream->error_flag = false;
|
||||
stream->eof_flag = false;
|
||||
}
|
||||
|
||||
int64_t filestream_read(RFILE *stream, void *s, int64_t len)
|
||||
@ -395,8 +404,6 @@ int64_t filestream_read(RFILE *stream, void *s, int64_t len)
|
||||
|
||||
if (output == VFS_ERROR_RETURN_VALUE)
|
||||
stream->error_flag = true;
|
||||
if (output < len)
|
||||
stream->eof_flag = true;
|
||||
|
||||
return output;
|
||||
}
|
||||
@ -494,9 +501,7 @@ int filestream_printf(RFILE *stream, const char* format, ...)
|
||||
|
||||
int filestream_error(RFILE *stream)
|
||||
{
|
||||
if (stream && stream->error_flag)
|
||||
return 1;
|
||||
return 0;
|
||||
return (stream && stream->error_flag);
|
||||
}
|
||||
|
||||
int filestream_close(RFILE *stream)
|
||||
@ -525,7 +530,7 @@ int filestream_close(RFILE *stream)
|
||||
*
|
||||
* Read the contents of a file into @buf.
|
||||
*
|
||||
* Returns: non zero on success.
|
||||
* @return Non-zero on success.
|
||||
*/
|
||||
int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
|
||||
{
|
||||
@ -542,24 +547,19 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
content_buf_size = filestream_get_size(file);
|
||||
|
||||
if (content_buf_size < 0)
|
||||
if ((content_buf_size = filestream_get_size(file)) < 0)
|
||||
goto error;
|
||||
|
||||
content_buf = malloc((size_t)(content_buf_size + 1));
|
||||
|
||||
if (!content_buf)
|
||||
if (!(content_buf = malloc((size_t)(content_buf_size + 1))))
|
||||
goto error;
|
||||
if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
|
||||
goto error;
|
||||
|
||||
ret = filestream_read(file, content_buf, (int64_t)content_buf_size);
|
||||
if (ret < 0)
|
||||
if ((ret = filestream_read(file, content_buf, (int64_t)content_buf_size)) <
|
||||
0)
|
||||
goto error;
|
||||
|
||||
if (filestream_close(file) != 0)
|
||||
if (file)
|
||||
free(file);
|
||||
|
||||
*buf = content_buf;
|
||||
@ -574,7 +574,6 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
|
||||
return 1;
|
||||
|
||||
error:
|
||||
if (file)
|
||||
if (filestream_close(file) != 0)
|
||||
free(file);
|
||||
if (content_buf)
|
||||
@ -593,8 +592,8 @@ error:
|
||||
*
|
||||
* Writes data to a file.
|
||||
*
|
||||
* Returns: true (1) on success, false (0) otherwise.
|
||||
*/
|
||||
* @return true on success, otherwise false.
|
||||
**/
|
||||
bool filestream_write_file(const char *path, const void *data, int64_t size)
|
||||
{
|
||||
int64_t ret = 0;
|
||||
@ -603,20 +602,18 @@ bool filestream_write_file(const char *path, const void *data, int64_t size)
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
ret = filestream_write(file, data, size);
|
||||
if (filestream_close(file) != 0)
|
||||
if (file)
|
||||
free(file);
|
||||
|
||||
if (ret != size)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return (ret == size);
|
||||
}
|
||||
|
||||
/* Returned pointer must be freed by the caller. */
|
||||
char* filestream_getline(RFILE *stream)
|
||||
/**
|
||||
* filestream_getline:
|
||||
*
|
||||
* Returned pointer must be freed by the caller.
|
||||
**/
|
||||
char *filestream_getline(RFILE *stream)
|
||||
{
|
||||
char *newline_tmp = NULL;
|
||||
size_t cur_size = 8;
|
||||
@ -638,9 +635,8 @@ char* filestream_getline(RFILE *stream)
|
||||
if (idx == cur_size)
|
||||
{
|
||||
cur_size *= 2;
|
||||
newline_tmp = (char*)realloc(newline, cur_size + 1);
|
||||
|
||||
if (!newline_tmp)
|
||||
if (!(newline_tmp = (char*)realloc(newline, cur_size + 1)))
|
||||
{
|
||||
free(newline);
|
||||
return NULL;
|
||||
|
@ -69,17 +69,27 @@ RFILE* rfopen(const char *path, const char *mode)
|
||||
|
||||
int rfclose(RFILE* stream)
|
||||
{
|
||||
if (!stream)
|
||||
return EOF;
|
||||
|
||||
return filestream_close(stream);
|
||||
}
|
||||
|
||||
int64_t rftell(RFILE* stream)
|
||||
{
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
return filestream_tell(stream);
|
||||
}
|
||||
|
||||
int64_t rfseek(RFILE* stream, int64_t offset, int origin)
|
||||
{
|
||||
int seek_position = -1;
|
||||
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case SEEK_SET:
|
||||
@ -99,32 +109,50 @@ int64_t rfseek(RFILE* stream, int64_t offset, int origin)
|
||||
int64_t rfread(void* buffer,
|
||||
size_t elem_size, size_t elem_count, RFILE* stream)
|
||||
{
|
||||
if (!stream || (elem_size == 0) || (elem_count == 0))
|
||||
return 0;
|
||||
|
||||
return (filestream_read(stream, buffer, elem_size * elem_count) / elem_size);
|
||||
}
|
||||
|
||||
char *rfgets(char *buffer, int maxCount, RFILE* stream)
|
||||
{
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
return filestream_gets(stream, buffer, maxCount);
|
||||
}
|
||||
|
||||
int rfgetc(RFILE* stream)
|
||||
{
|
||||
if (!stream)
|
||||
return EOF;
|
||||
|
||||
return filestream_getc(stream);
|
||||
}
|
||||
|
||||
int64_t rfwrite(void const* buffer,
|
||||
size_t elem_size, size_t elem_count, RFILE* stream)
|
||||
{
|
||||
return filestream_write(stream, buffer, elem_size * elem_count);
|
||||
if (!stream || (elem_size == 0) || (elem_count == 0))
|
||||
return 0;
|
||||
|
||||
return (filestream_write(stream, buffer, elem_size * elem_count) / elem_size);
|
||||
}
|
||||
|
||||
int rfputc(int character, RFILE * stream)
|
||||
{
|
||||
if (!stream)
|
||||
return EOF;
|
||||
|
||||
return filestream_putc(stream, character);
|
||||
}
|
||||
|
||||
int64_t rfflush(RFILE * stream)
|
||||
{
|
||||
if (!stream)
|
||||
return EOF;
|
||||
|
||||
return filestream_flush(stream);
|
||||
}
|
||||
|
||||
@ -132,6 +160,10 @@ int rfprintf(RFILE * stream, const char * format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list vl;
|
||||
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
va_start(vl, format);
|
||||
result = filestream_vprintf(stream, format, vl);
|
||||
va_end(vl);
|
||||
@ -152,8 +184,12 @@ int rfscanf(RFILE * stream, const char * format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list vl;
|
||||
|
||||
if (!stream)
|
||||
return 0;
|
||||
|
||||
va_start(vl, format);
|
||||
result = filestream_scanf(stream, format, vl);
|
||||
result = filestream_vscanf(stream, format, &vl);
|
||||
va_end(vl);
|
||||
return result;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <compat/strl.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <encodings/utf.h>
|
||||
|
||||
@ -89,9 +90,11 @@ char *string_ucwords(char *s)
|
||||
}
|
||||
|
||||
char *string_replace_substring(const char *in,
|
||||
const char *pattern, const char *replacement)
|
||||
const char *pattern, size_t pattern_len,
|
||||
const char *replacement, size_t replacement_len)
|
||||
{
|
||||
size_t numhits, pattern_len, replacement_len, outlen;
|
||||
size_t outlen;
|
||||
size_t numhits = 0;
|
||||
const char *inat = NULL;
|
||||
const char *inprev = NULL;
|
||||
char *out = NULL;
|
||||
@ -102,9 +105,6 @@ char *string_replace_substring(const char *in,
|
||||
if (!pattern || !replacement)
|
||||
return strdup(in);
|
||||
|
||||
pattern_len = strlen(pattern);
|
||||
replacement_len = strlen(replacement);
|
||||
numhits = 0;
|
||||
inat = in;
|
||||
|
||||
while ((inat = strstr(inat, pattern)))
|
||||
@ -114,9 +114,8 @@ char *string_replace_substring(const char *in,
|
||||
}
|
||||
|
||||
outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits;
|
||||
out = (char *)malloc(outlen+1);
|
||||
|
||||
if (!out)
|
||||
if (!(out = (char *)malloc(outlen+1)))
|
||||
return NULL;
|
||||
|
||||
outat = out;
|
||||
@ -137,7 +136,11 @@ char *string_replace_substring(const char *in,
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Remove leading whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace_left:
|
||||
*
|
||||
* Remove leading whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace_left(char *const s)
|
||||
{
|
||||
if (s && *s)
|
||||
@ -158,7 +161,11 @@ char *string_trim_whitespace_left(char *const s)
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Remove trailing whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace_right:
|
||||
*
|
||||
* Remove trailing whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace_right(char *const s)
|
||||
{
|
||||
if (s && *s)
|
||||
@ -178,7 +185,11 @@ char *string_trim_whitespace_right(char *const s)
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Remove leading and trailing whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace:
|
||||
*
|
||||
* Remove leading and trailing whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace(char *const s)
|
||||
{
|
||||
string_trim_whitespace_right(s); /* order matters */
|
||||
@ -187,35 +198,51 @@ char *string_trim_whitespace(char *const s)
|
||||
return s;
|
||||
}
|
||||
|
||||
void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int wideglyph_width, unsigned max_lines)
|
||||
/**
|
||||
* word_wrap:
|
||||
* @dst : pointer to destination buffer.
|
||||
* @dst_size : size of destination buffer.
|
||||
* @src : pointer to input string.
|
||||
* @line_width : max number of characters per line.
|
||||
* @wideglyph_width : not used, but is necessary to keep
|
||||
* compatibility with word_wrap_wideglyph().
|
||||
* @max_lines : max lines of destination string.
|
||||
* 0 means no limit.
|
||||
*
|
||||
* Wraps string specified by 'src' to destination buffer
|
||||
* specified by 'dst' and 'dst_size'.
|
||||
* This function assumes that all glyphs in the string
|
||||
* have an on-screen pixel width similar to that of
|
||||
* regular Latin characters - i.e. it will not wrap
|
||||
* correctly any text containing so-called 'wide' Unicode
|
||||
* characters (e.g. CJK languages, emojis, etc.).
|
||||
**/
|
||||
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;
|
||||
size_t src_len = strlen(src);
|
||||
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 < line_width)
|
||||
{
|
||||
strcpy(dst, src);
|
||||
return;
|
||||
}
|
||||
if (src_len < (size_t)line_width)
|
||||
return strlcpy(dst, src, dst_size);
|
||||
|
||||
while (*src != '\0')
|
||||
{
|
||||
unsigned char_len;
|
||||
|
||||
char_len = (unsigned)(utf8skip(src, 1) - src);
|
||||
unsigned char_len = (unsigned)(utf8skip(src, 1) - src);
|
||||
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,
|
||||
@ -226,10 +253,7 @@ void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int
|
||||
/* Early return if remaining src string
|
||||
* length is less than line width */
|
||||
if (src_end - src <= line_width)
|
||||
{
|
||||
strcpy(dst, src);
|
||||
return;
|
||||
}
|
||||
return strlcpy(dst, src, dst_size);
|
||||
}
|
||||
|
||||
while (char_len--)
|
||||
@ -239,36 +263,69 @@ void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int
|
||||
{
|
||||
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)
|
||||
{
|
||||
strcpy(dst, src);
|
||||
return;
|
||||
}
|
||||
return strlcpy(dst, src, dst_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*dst = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_width, int wideglyph_width, unsigned max_lines)
|
||||
/**
|
||||
* word_wrap_wideglyph:
|
||||
* @dst : pointer to destination buffer.
|
||||
* @dst_size : size of destination buffer.
|
||||
* @src : pointer to input string.
|
||||
* @line_width : max number of characters per line.
|
||||
* @wideglyph_width : effective width of 'wide' Unicode glyphs.
|
||||
* the value here is normalised relative to the
|
||||
* typical on-screen pixel width of a regular
|
||||
* Latin character:
|
||||
* - a regular Latin character is defined to
|
||||
* have an effective width of 100
|
||||
* - wideglyph_width = 100 * (wide_character_pixel_width / latin_character_pixel_width)
|
||||
* - e.g. if 'wide' Unicode characters in 'src'
|
||||
* have an on-screen pixel width twice that of
|
||||
* regular Latin characters, wideglyph_width
|
||||
* would be 200
|
||||
* @max_lines : max lines of destination string.
|
||||
* 0 means no limit.
|
||||
*
|
||||
* Wraps string specified by @src to destination buffer
|
||||
* specified by @dst and @dst_size.
|
||||
* This function assumes that all glyphs in the string
|
||||
* are:
|
||||
* - EITHER 'non-wide' Unicode glyphs, with an on-screen
|
||||
* pixel width similar to that of regular Latin characters
|
||||
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
|
||||
* with an on-screen pixel width defined by @wideglyph_width
|
||||
* Note that wrapping may occur in inappropriate locations
|
||||
* if @src string contains 'wide' Unicode characters whose
|
||||
* on-screen pixel width deviates greatly from the set
|
||||
* @wideglyph_width value.
|
||||
**/
|
||||
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)
|
||||
{
|
||||
char *lastspace = NULL;
|
||||
char *lastwideglyph = NULL;
|
||||
const char *src_end = src + strlen(src);
|
||||
const char *src_end = src + src_len;
|
||||
unsigned lines = 1;
|
||||
/* 'line_width' means max numbers of characters per line,
|
||||
* but this metric is only meaningful when dealing with
|
||||
@ -298,16 +355,11 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
|
||||
/* 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')
|
||||
{
|
||||
unsigned char_len;
|
||||
|
||||
char_len = (unsigned)(utf8skip(src, 1) - src);
|
||||
unsigned char_len = (unsigned)(utf8skip(src, 1) - src);
|
||||
counter_normalized += 100;
|
||||
|
||||
/* Prevent buffer overflow */
|
||||
@ -326,10 +378,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
|
||||
/* 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)
|
||||
{
|
||||
@ -361,10 +410,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
|
||||
/* 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)
|
||||
{
|
||||
@ -379,21 +425,22 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Splits string into tokens seperated by 'delim'
|
||||
/**
|
||||
* string_tokenize:
|
||||
*
|
||||
* Splits string into tokens seperated by @delim
|
||||
* > Returned token string must be free()'d
|
||||
* > Returns NULL if token is not found
|
||||
* > After each call, 'str' is set to the position after the
|
||||
* > After each call, @str is set to the position after the
|
||||
* last found token
|
||||
* > Tokens *include* empty strings
|
||||
* Usage example:
|
||||
@ -406,7 +453,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
|
||||
* free(token);
|
||||
* token = NULL;
|
||||
* }
|
||||
*/
|
||||
**/
|
||||
char* string_tokenize(char **str, const char *delim)
|
||||
{
|
||||
/* Taken from https://codereview.stackexchange.com/questions/216956/strtok-function-thread-safe-supports-empty-tokens-doesnt-change-string# */
|
||||
@ -419,25 +466,20 @@ char* string_tokenize(char **str, const char *delim)
|
||||
if (!str || string_is_empty(delim))
|
||||
return NULL;
|
||||
|
||||
str_ptr = *str;
|
||||
|
||||
/* Note: we don't check string_is_empty() here,
|
||||
* empty strings are valid */
|
||||
if (!str_ptr)
|
||||
if (!(str_ptr = *str))
|
||||
return NULL;
|
||||
|
||||
/* Search for delimiter */
|
||||
delim_ptr = strstr(str_ptr, delim);
|
||||
|
||||
if (delim_ptr)
|
||||
if ((delim_ptr = strstr(str_ptr, delim)))
|
||||
token_len = delim_ptr - str_ptr;
|
||||
else
|
||||
token_len = strlen(str_ptr);
|
||||
|
||||
/* Allocate token string */
|
||||
token = (char *)malloc((token_len + 1) * sizeof(char));
|
||||
|
||||
if (!token)
|
||||
if (!(token = (char *)malloc((token_len + 1) * sizeof(char))))
|
||||
return NULL;
|
||||
|
||||
/* Copy token */
|
||||
@ -450,42 +492,53 @@ char* string_tokenize(char **str, const char *delim)
|
||||
return token;
|
||||
}
|
||||
|
||||
/* Removes every instance of character 'c' from 'str' */
|
||||
/**
|
||||
* string_remove_all_chars:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Removes every instance of character @c from @str
|
||||
**/
|
||||
void string_remove_all_chars(char *str, char c)
|
||||
{
|
||||
char *read_ptr = NULL;
|
||||
char *write_ptr = NULL;
|
||||
|
||||
if (string_is_empty(str))
|
||||
return;
|
||||
|
||||
read_ptr = str;
|
||||
write_ptr = str;
|
||||
char *read_ptr = str;
|
||||
char *write_ptr = str;
|
||||
|
||||
while (*read_ptr != '\0')
|
||||
{
|
||||
*write_ptr = *read_ptr++;
|
||||
write_ptr += (*write_ptr != c) ? 1 : 0;
|
||||
if (*write_ptr != c)
|
||||
write_ptr++;
|
||||
}
|
||||
|
||||
*write_ptr = '\0';
|
||||
}
|
||||
|
||||
/* Replaces every instance of character 'find' in 'str'
|
||||
* with character 'replace' */
|
||||
/**
|
||||
* string_replace_all_chars:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
* @find : character to find
|
||||
* @replace : character to replace @find with
|
||||
*
|
||||
* Replaces every instance of character @find in @str
|
||||
* with character @replace
|
||||
**/
|
||||
void string_replace_all_chars(char *str, char find, char replace)
|
||||
{
|
||||
char *str_ptr = str;
|
||||
|
||||
if (string_is_empty(str))
|
||||
return;
|
||||
|
||||
while ((str_ptr = strchr(str_ptr, find)))
|
||||
*str_ptr++ = replace;
|
||||
}
|
||||
|
||||
/* Converts string to unsigned integer.
|
||||
* Returns 0 if string is invalid */
|
||||
/**
|
||||
* string_to_unsigned:
|
||||
* @str : input string
|
||||
*
|
||||
* Converts string to unsigned integer.
|
||||
*
|
||||
* @return 0 if string is invalid, otherwise > 0
|
||||
**/
|
||||
unsigned string_to_unsigned(const char *str)
|
||||
{
|
||||
const char *ptr = NULL;
|
||||
@ -502,28 +555,34 @@ unsigned string_to_unsigned(const char *str)
|
||||
return (unsigned)strtoul(str, NULL, 10);
|
||||
}
|
||||
|
||||
/* Converts hexadecimal string to unsigned integer.
|
||||
/**
|
||||
* string_hex_to_unsigned:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
*
|
||||
* Converts hexadecimal string to unsigned integer.
|
||||
* Handles optional leading '0x'.
|
||||
* Returns 0 if string is invalid */
|
||||
*
|
||||
* @return 0 if string is invalid, otherwise > 0
|
||||
**/
|
||||
unsigned string_hex_to_unsigned(const char *str)
|
||||
{
|
||||
const char *hex_str = str;
|
||||
const char *ptr = NULL;
|
||||
size_t len;
|
||||
|
||||
if (string_is_empty(str))
|
||||
return 0;
|
||||
|
||||
/* Remove leading '0x', if required */
|
||||
len = strlen(str);
|
||||
|
||||
if (len >= 2)
|
||||
if ((str[0] == '0') &&
|
||||
((str[1] == 'x') || (str[1] == 'X')))
|
||||
if (str[0] != '\0' && str[1] != '\0')
|
||||
{
|
||||
if ( (str[0] == '0') &&
|
||||
((str[1] == 'x') ||
|
||||
(str[1] == 'X')))
|
||||
{
|
||||
hex_str = str + 2;
|
||||
|
||||
if (string_is_empty(hex_str))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
/* Check for valid characters */
|
||||
for (ptr = hex_str; *ptr != '\0'; ptr++)
|
||||
@ -534,3 +593,117 @@ unsigned string_hex_to_unsigned(const char *str)
|
||||
|
||||
return (unsigned)strtoul(hex_str, NULL, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* string_count_occurrences_single_character:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Get the total number of occurrences of character @c in @str.
|
||||
*
|
||||
* @return Total number of occurrences of character @c
|
||||
*/
|
||||
int string_count_occurrences_single_character(const char *str, char c)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (; *str; str++)
|
||||
if (*str == c)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_replace_whitespace_with_single_character:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Replaces all spaces with given character @c.
|
||||
**/
|
||||
void string_replace_whitespace_with_single_character(char *str, char c)
|
||||
{
|
||||
for (; *str; str++)
|
||||
if (ISSPACE(*str))
|
||||
*str = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_replace_multi_space_with_single_space:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Replaces multiple spaces with a single space in a string.
|
||||
**/
|
||||
void string_replace_multi_space_with_single_space(char *str)
|
||||
{
|
||||
char *str_trimmed = str;
|
||||
bool prev_is_space = false;
|
||||
bool curr_is_space = false;
|
||||
|
||||
for (; *str; str++)
|
||||
{
|
||||
curr_is_space = ISSPACE(*str);
|
||||
if (prev_is_space && curr_is_space)
|
||||
continue;
|
||||
*str_trimmed++ = *str;
|
||||
prev_is_space = curr_is_space;
|
||||
}
|
||||
*str_trimmed = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* string_remove_all_whitespace:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Remove all spaces from the given string.
|
||||
**/
|
||||
void string_remove_all_whitespace(char *str_trimmed, const char *str)
|
||||
{
|
||||
for (; *str; str++)
|
||||
if (!ISSPACE(*str))
|
||||
*str_trimmed++ = *str;
|
||||
*str_trimmed = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last occurance of the given character in a string.
|
||||
*/
|
||||
int string_index_last_occurance(const char *str, char c)
|
||||
{
|
||||
const char *pos = strrchr(str, c);
|
||||
if (pos)
|
||||
return (int)(pos - str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_find_index_substring_string:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
* @substr : substring to find in @str
|
||||
*
|
||||
* Find the position of substring @substr in string @str.
|
||||
**/
|
||||
int string_find_index_substring_string(const char *str, const char *substr)
|
||||
{
|
||||
const char *pos = strstr(str, substr);
|
||||
if (pos)
|
||||
return (int)(pos - str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_copy_only_ascii:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Strips non-ASCII characters from a string.
|
||||
**/
|
||||
void string_copy_only_ascii(char *str_stripped, const char *str)
|
||||
{
|
||||
for (; *str; str++)
|
||||
if (*str > 0x1F && *str < 0x7F)
|
||||
*str_stripped++ = *str;
|
||||
*str_stripped = '\0';
|
||||
}
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
#include <rthreads/rthreads.h>
|
||||
#include <retro_assert.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
@ -41,8 +40,6 @@ void rtime_init(void)
|
||||
#ifdef HAVE_THREADS
|
||||
if (!rtime_localtime_lock)
|
||||
rtime_localtime_lock = slock_new();
|
||||
|
||||
retro_assert(rtime_localtime_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string/stdstring.h>
|
||||
#include <string/stdstring.h> /* string_is_empty */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
@ -57,11 +57,6 @@
|
||||
# include <dirent.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
# if defined(ORBIS)
|
||||
# include <sys/fcntl.h>
|
||||
# include <sys/dirent.h>
|
||||
# include <orbisFile.h>
|
||||
# endif
|
||||
# if defined(WIIU)
|
||||
# include <malloc.h>
|
||||
# endif
|
||||
@ -74,11 +69,6 @@
|
||||
# include <psp2/io/fcntl.h>
|
||||
# include <psp2/io/dirent.h>
|
||||
# include <psp2/io/stat.h>
|
||||
#elif defined(ORBIS)
|
||||
# include <orbisFile.h>
|
||||
# include <ps4link.h>
|
||||
# include <sys/dirent.h>
|
||||
# include <sys/fcntl.h>
|
||||
#elif !defined(_WIN32)
|
||||
# if defined(PSP)
|
||||
# include <pspiofilemgr.h>
|
||||
@ -124,19 +114,66 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(ORBIS)
|
||||
#include <orbisFile.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/dirent.h>
|
||||
#endif
|
||||
|
||||
#if defined(PSP)
|
||||
#include <pspkernel.h>
|
||||
#endif
|
||||
|
||||
#if defined(__PS3__) || defined(__PSL1GHT__)
|
||||
#include <defines/ps3_defines.h>
|
||||
#if defined(__PSL1GHT__)
|
||||
#define FS_SUCCEEDED 0
|
||||
#define FS_TYPE_DIR 1
|
||||
#ifdef __PSL1GHT__
|
||||
#include <lv2/sysfs.h>
|
||||
#ifndef O_RDONLY
|
||||
#define O_RDONLY SYS_O_RDONLY
|
||||
#endif
|
||||
#ifndef O_WRONLY
|
||||
#define O_WRONLY SYS_O_WRONLY
|
||||
#endif
|
||||
#ifndef O_CREAT
|
||||
#define O_CREAT SYS_O_CREAT
|
||||
#endif
|
||||
#ifndef O_TRUNC
|
||||
#define O_TRUNC SYS_O_TRUNC
|
||||
#endif
|
||||
#ifndef O_RDWR
|
||||
#define O_RDWR SYS_O_RDWR
|
||||
#endif
|
||||
#else
|
||||
#include <cell/cell_fs.h>
|
||||
#ifndef O_RDONLY
|
||||
#define O_RDONLY CELL_FS_O_RDONLY
|
||||
#endif
|
||||
#ifndef O_WRONLY
|
||||
#define O_WRONLY CELL_FS_O_WRONLY
|
||||
#endif
|
||||
#ifndef O_CREAT
|
||||
#define O_CREAT CELL_FS_O_CREAT
|
||||
#endif
|
||||
#ifndef O_TRUNC
|
||||
#define O_TRUNC CELL_FS_O_TRUNC
|
||||
#endif
|
||||
#ifndef O_RDWR
|
||||
#define O_RDWR CELL_FS_O_RDWR
|
||||
#endif
|
||||
#ifndef sysFsStat
|
||||
#define sysFsStat cellFsStat
|
||||
#endif
|
||||
#ifndef sysFSDirent
|
||||
#define sysFSDirent CellFsDirent
|
||||
#endif
|
||||
#ifndef sysFsOpendir
|
||||
#define sysFsOpendir cellFsOpendir
|
||||
#endif
|
||||
#ifndef sysFsReaddir
|
||||
#define sysFsReaddir cellFsReaddir
|
||||
#endif
|
||||
#ifndef sysFSDirent
|
||||
#define sysFSDirent CellFsDirent
|
||||
#endif
|
||||
#ifndef sysFsClosedir
|
||||
#define sysFsClosedir cellFsClosedir
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -200,13 +237,6 @@ int64_t retro_vfs_file_seek_internal(
|
||||
#ifdef ATLEAST_VC2005
|
||||
/* VC2005 and up have a special 64-bit fseek */
|
||||
return _fseeki64(stream->fp, offset, whence);
|
||||
#elif defined(ORBIS)
|
||||
{
|
||||
int ret = orbisLseek(stream->fd, offset, whence);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#elif defined(HAVE_64BIT_OFFSETS)
|
||||
return fseeko(stream->fp, (off_t)offset, whence);
|
||||
#else
|
||||
@ -269,19 +299,6 @@ int64_t retro_vfs_file_seek_internal(
|
||||
libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
const char *path, unsigned mode, unsigned hints)
|
||||
{
|
||||
#if defined(VFS_FRONTEND) || defined(HAVE_CDROM)
|
||||
int path_len = (int)strlen(path);
|
||||
#endif
|
||||
#ifdef VFS_FRONTEND
|
||||
const char *dumb_prefix = "vfsonly://";
|
||||
size_t dumb_prefix_siz = STRLEN_CONST("vfsonly://");
|
||||
int dumb_prefix_len = (int)dumb_prefix_siz;
|
||||
#endif
|
||||
#ifdef HAVE_CDROM
|
||||
const char *cdrom_prefix = "cdrom://";
|
||||
size_t cdrom_prefix_siz = STRLEN_CONST("cdrom://");
|
||||
int cdrom_prefix_len = (int)cdrom_prefix_siz;
|
||||
#endif
|
||||
int flags = 0;
|
||||
const char *mode_str = NULL;
|
||||
libretro_vfs_implementation_file *stream =
|
||||
@ -306,9 +323,18 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
stream->scheme = VFS_SCHEME_NONE;
|
||||
|
||||
#ifdef VFS_FRONTEND
|
||||
if (path_len >= dumb_prefix_len)
|
||||
if (!memcmp(path, dumb_prefix, dumb_prefix_len))
|
||||
path += dumb_prefix_siz;
|
||||
if ( path
|
||||
&& path[0] == 'v'
|
||||
&& path[1] == 'f'
|
||||
&& path[2] == 's'
|
||||
&& path[3] == 'o'
|
||||
&& path[4] == 'n'
|
||||
&& path[5] == 'l'
|
||||
&& path[6] == 'y'
|
||||
&& path[7] == ':'
|
||||
&& path[8] == '/'
|
||||
&& path[9] == '/')
|
||||
path += sizeof("vfsonly://")-1;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CDROM
|
||||
@ -325,14 +351,20 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
stream->cdrom.last_frame[0] = '\0';
|
||||
stream->cdrom.last_frame_valid = false;
|
||||
|
||||
if (path_len > cdrom_prefix_len)
|
||||
if ( path
|
||||
&& path[0] == 'c'
|
||||
&& path[1] == 'd'
|
||||
&& path[2] == 'r'
|
||||
&& path[3] == 'o'
|
||||
&& path[4] == 'm'
|
||||
&& path[5] == ':'
|
||||
&& path[6] == '/'
|
||||
&& path[7] == '/'
|
||||
&& path[8] != '\0')
|
||||
{
|
||||
if (!memcmp(path, cdrom_prefix, cdrom_prefix_len))
|
||||
{
|
||||
path += cdrom_prefix_siz;
|
||||
path += sizeof("cdrom://")-1;
|
||||
stream->scheme = VFS_SCHEME_CDROM;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
stream->orig_path = strdup(path);
|
||||
@ -359,24 +391,20 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
mode_str = "wb";
|
||||
|
||||
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
#if !defined(ORBIS)
|
||||
#if !defined(_WIN32)
|
||||
flags |= S_IRUSR | S_IWUSR;
|
||||
#else
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RETRO_VFS_FILE_ACCESS_READ_WRITE:
|
||||
mode_str = "w+b";
|
||||
flags = O_RDWR | O_CREAT | O_TRUNC;
|
||||
#if !defined(ORBIS)
|
||||
#if !defined(_WIN32)
|
||||
flags |= S_IRUSR | S_IWUSR;
|
||||
#else
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
|
||||
@ -385,12 +413,10 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
mode_str = "r+b";
|
||||
|
||||
flags = O_RDWR;
|
||||
#if !defined(ORBIS)
|
||||
#if !defined(_WIN32)
|
||||
flags |= S_IRUSR | S_IWUSR;
|
||||
#else
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
|
||||
@ -400,15 +426,6 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
#ifdef ORBIS
|
||||
int fd = orbisOpen(path, flags, 0644);
|
||||
if (fd < 0)
|
||||
{
|
||||
stream->fd = -1;
|
||||
goto error;
|
||||
}
|
||||
stream->fd = fd;
|
||||
#else
|
||||
FILE *fp;
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
@ -425,13 +442,12 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
else
|
||||
#endif
|
||||
{
|
||||
fp = (FILE*)fopen_utf8(path, mode_str);
|
||||
|
||||
if (!fp)
|
||||
if (!(fp = (FILE*)fopen_utf8(path, mode_str)))
|
||||
goto error;
|
||||
|
||||
stream->fp = fp;
|
||||
}
|
||||
|
||||
/* Regarding setvbuf:
|
||||
*
|
||||
* https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html
|
||||
@ -455,19 +471,14 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
#elif defined(WIIU)
|
||||
if (stream->scheme != VFS_SCHEME_CDROM)
|
||||
{
|
||||
const int bufsize = 128*1024;
|
||||
const int bufsize = 128 * 1024;
|
||||
stream->buf = (char*)memalign(0x40, bufsize);
|
||||
if (stream->fp)
|
||||
setvbuf(stream->fp, stream->buf, _IOFBF, bufsize);
|
||||
}
|
||||
#elif !defined(PSP)
|
||||
if (stream->scheme != VFS_SCHEME_CDROM)
|
||||
{
|
||||
stream->buf = (char*)calloc(1, 0x4000);
|
||||
if (stream->fp)
|
||||
setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else
|
||||
@ -503,18 +514,12 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
|
||||
retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
|
||||
|
||||
stream->mapped = (uint8_t*)mmap((void*)0,
|
||||
stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0);
|
||||
|
||||
if (stream->mapped == MAP_FAILED)
|
||||
if ((stream->mapped = (uint8_t*)mmap((void*)0,
|
||||
stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0)) == MAP_FAILED)
|
||||
stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef ORBIS
|
||||
stream->size = orbisLseek(stream->fd, 0, SEEK_END);
|
||||
orbisLseek(stream->fd, 0, SEEK_SET);
|
||||
#else
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
{
|
||||
@ -535,7 +540,6 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
|
||||
retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
|
||||
}
|
||||
#endif
|
||||
return stream;
|
||||
|
||||
error:
|
||||
@ -570,14 +574,7 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
|
||||
}
|
||||
|
||||
if (stream->fd > 0)
|
||||
{
|
||||
#ifdef ORBIS
|
||||
orbisClose(stream->fd);
|
||||
stream->fd = -1;
|
||||
#else
|
||||
close(stream->fd);
|
||||
#endif
|
||||
}
|
||||
#ifdef HAVE_CDROM
|
||||
end:
|
||||
if (stream->cdrom.cue_buf)
|
||||
@ -600,12 +597,7 @@ int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream)
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
return retro_vfs_file_error_cdrom(stream);
|
||||
#endif
|
||||
#ifdef ORBIS
|
||||
/* TODO/FIXME - implement this? */
|
||||
return 0;
|
||||
#else
|
||||
return ferror(stream->fp);
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
|
||||
@ -617,18 +609,20 @@ int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
|
||||
|
||||
int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length)
|
||||
{
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (_chsize(_fileno(stream->fp), length) != 0)
|
||||
return -1;
|
||||
#elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX))
|
||||
if (ftruncate(fileno(stream->fp), (off_t)length) != 0)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
if (stream && _chsize(_fileno(stream->fp), length) == 0)
|
||||
{
|
||||
stream->size = length;
|
||||
return 0;
|
||||
}
|
||||
#elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX))
|
||||
if (stream && ftruncate(fileno(stream->fp), (off_t)length) == 0)
|
||||
{
|
||||
stream->size = length;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
|
||||
@ -642,14 +636,6 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
return retro_vfs_file_tell_cdrom(stream);
|
||||
#endif
|
||||
#ifdef ORBIS
|
||||
{
|
||||
int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#ifdef ATLEAST_VC2005
|
||||
/* VC2005 and up have a special 64-bit ftell */
|
||||
return _ftelli64(stream->fp);
|
||||
@ -657,7 +643,6 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
|
||||
return ftello(stream->fp);
|
||||
#else
|
||||
return ftell(stream->fp);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#ifdef HAVE_MMAP
|
||||
@ -676,21 +661,7 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
|
||||
int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream,
|
||||
int64_t offset, int seek_position)
|
||||
{
|
||||
int whence = -1;
|
||||
switch (seek_position)
|
||||
{
|
||||
case RETRO_VFS_SEEK_POSITION_START:
|
||||
whence = SEEK_SET;
|
||||
break;
|
||||
case RETRO_VFS_SEEK_POSITION_CURRENT:
|
||||
whence = SEEK_CUR;
|
||||
break;
|
||||
case RETRO_VFS_SEEK_POSITION_END:
|
||||
whence = SEEK_END;
|
||||
break;
|
||||
}
|
||||
|
||||
return retro_vfs_file_seek_internal(stream, offset, whence);
|
||||
return retro_vfs_file_seek_internal(stream, offset, seek_position);
|
||||
}
|
||||
|
||||
int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
|
||||
@ -705,13 +676,7 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
return retro_vfs_file_read_cdrom(stream, s, len);
|
||||
#endif
|
||||
#ifdef ORBIS
|
||||
if (orbisRead(stream->fd, s, (size_t)len) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
#else
|
||||
return fread(s, 1, (size_t)len, stream->fp);
|
||||
#endif
|
||||
}
|
||||
#ifdef HAVE_MMAP
|
||||
if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
|
||||
@ -734,83 +699,80 @@ 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;
|
||||
ssize_t result = -1;
|
||||
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
#ifdef ORBIS
|
||||
if (orbisWrite(stream->fd, s, (size_t)len) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
#else
|
||||
return fwrite(s, 1, (size_t)len, stream->fp);
|
||||
#endif
|
||||
}
|
||||
pos = retro_vfs_file_tell_impl(stream);
|
||||
result = fwrite(s, 1, (size_t)len, stream->fp);
|
||||
|
||||
if (result != -1 && pos + result > stream->size)
|
||||
stream->size = pos + result;
|
||||
|
||||
return result;
|
||||
}
|
||||
#ifdef HAVE_MMAP
|
||||
if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
|
||||
return -1;
|
||||
#endif
|
||||
return write(stream->fd, s, (size_t)len);
|
||||
|
||||
pos = retro_vfs_file_tell_impl(stream);
|
||||
result = write(stream->fd, s, (size_t)len);
|
||||
|
||||
if (result != -1 && pos + result > stream->size)
|
||||
stream->size = pos + result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream)
|
||||
{
|
||||
if (!stream)
|
||||
return -1;
|
||||
#ifdef ORBIS
|
||||
if (stream && fflush(stream->fp) == 0)
|
||||
return 0;
|
||||
#else
|
||||
return fflush(stream->fp) == 0 ? 0 : -1;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int retro_vfs_file_remove_impl(const char *path)
|
||||
{
|
||||
if (path && *path)
|
||||
{
|
||||
int ret = -1;
|
||||
#if defined(_WIN32) && !defined(_XBOX)
|
||||
/* Win32 (no Xbox) */
|
||||
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
|
||||
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;
|
||||
#endif
|
||||
if (!path || !*path)
|
||||
return -1;
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
|
||||
path_local = utf8_to_local_string_alloc(path);
|
||||
|
||||
if (path_local)
|
||||
if ((path_wide = utf8_to_utf16_string_alloc(path)))
|
||||
{
|
||||
int ret = remove(path_local);
|
||||
free(path_local);
|
||||
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
path_wide = utf8_to_utf16_string_alloc(path);
|
||||
|
||||
if (path_wide)
|
||||
{
|
||||
int ret = _wremove(path_wide);
|
||||
/* 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 (ret == 0)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
#elif defined(ORBIS)
|
||||
/* Orbis
|
||||
* TODO/FIXME - stub for now */
|
||||
return 0;
|
||||
#else
|
||||
if (remove(path) == 0)
|
||||
return 0;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
|
||||
@ -862,13 +824,6 @@ int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
|
||||
#endif
|
||||
return ret;
|
||||
|
||||
#elif defined(ORBIS)
|
||||
/* Orbis */
|
||||
/* TODO/FIXME - Stub for now */
|
||||
if (!old_path || !*old_path || !new_path || !*new_path)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
/* Every other platform */
|
||||
if (!old_path || !*old_path || !new_path || !*new_path)
|
||||
@ -888,154 +843,123 @@ 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;
|
||||
#if defined(VITA) || defined(PSP)
|
||||
/* Vita / PSP */
|
||||
SceIoStat buf;
|
||||
int dir_ret;
|
||||
char *tmp = NULL;
|
||||
size_t len = 0;
|
||||
int ret = RETRO_VFS_STAT_IS_VALID;
|
||||
|
||||
if (!path || !*path)
|
||||
return 0;
|
||||
|
||||
tmp = strdup(path);
|
||||
len = strlen(tmp);
|
||||
{
|
||||
#if defined(VITA)
|
||||
/* 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';
|
||||
|
||||
dir_ret = sceIoGetstat(tmp, &buf);
|
||||
dir_ret = sceIoGetstat(tmp, &stat_buf);
|
||||
free(tmp);
|
||||
if (dir_ret < 0)
|
||||
return 0;
|
||||
|
||||
if (size)
|
||||
*size = (int32_t)buf.st_size;
|
||||
*size = (int32_t)stat_buf.st_size;
|
||||
|
||||
is_dir = FIO_S_ISDIR(buf.st_mode);
|
||||
#elif defined(ORBIS)
|
||||
/* Orbis */
|
||||
int dir_ret = 0;
|
||||
|
||||
if (!path || !*path)
|
||||
return 0;
|
||||
|
||||
if (size)
|
||||
*size = (int32_t)buf.st_size;
|
||||
|
||||
dir_ret = orbisDopen(path);
|
||||
is_dir = dir_ret > 0;
|
||||
orbisDclose(dir_ret);
|
||||
|
||||
is_character_special = S_ISCHR(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;
|
||||
sysFSStat stat_buf;
|
||||
|
||||
if (!path || !*path)
|
||||
return 0;
|
||||
if (sysFsStat(path, &buf) < 0)
|
||||
return 0;
|
||||
|
||||
if (size)
|
||||
*size = (int32_t)buf.st_size;
|
||||
|
||||
is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR);
|
||||
#elif defined(_WIN32)
|
||||
/* Windows */
|
||||
DWORD file_info;
|
||||
struct _stat buf;
|
||||
#if defined(LEGACY_WIN32)
|
||||
char *path_local = NULL;
|
||||
#else
|
||||
wchar_t *path_wide = NULL;
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
#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;
|
||||
|
||||
if (string_is_empty(path))
|
||||
return 0;
|
||||
|
||||
path_buf = strdup(path);
|
||||
if (!path_buf)
|
||||
return 0;
|
||||
|
||||
len = strlen(path_buf);
|
||||
if (len > 0)
|
||||
if (path_buf[len - 1] == '/')
|
||||
path_buf[len - 1] = '\0';
|
||||
|
||||
stat_ret = stat(path_buf, &stat_buf);
|
||||
free(path_buf);
|
||||
|
||||
if (stat_ret < 0)
|
||||
if (sysFsStat(path, &stat_buf) < 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);
|
||||
if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
|
||||
ret |= RETRO_VFS_STAT_IS_DIRECTORY;
|
||||
#elif defined(_WIN32)
|
||||
/* Windows */
|
||||
struct _stat stat_buf;
|
||||
#if defined(LEGACY_WIN32)
|
||||
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
|
||||
/* Every other platform */
|
||||
struct stat buf;
|
||||
wchar_t *path_wide = utf8_to_utf16_string_alloc(path);
|
||||
DWORD file_info = GetFileAttributesW(path_wide);
|
||||
|
||||
if (!path || !*path)
|
||||
return 0;
|
||||
if (stat(path, &buf) < 0)
|
||||
_wstat(path_wide, &stat_buf);
|
||||
|
||||
if (path_wide)
|
||||
free(path_wide);
|
||||
#endif
|
||||
if (file_info == INVALID_FILE_ATTRIBUTES)
|
||||
return 0;
|
||||
|
||||
if (size)
|
||||
*size = (int32_t)buf.st_size;
|
||||
*size = (int32_t)stat_buf.st_size;
|
||||
|
||||
is_dir = S_ISDIR(buf.st_mode);
|
||||
is_character_special = S_ISCHR(buf.st_mode);
|
||||
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 */
|
||||
size_t len;
|
||||
char *path_buf = NULL;
|
||||
struct stat stat_buf;
|
||||
|
||||
if (!(path_buf = strdup(path)))
|
||||
return 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;
|
||||
}
|
||||
|
||||
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 stat_buf;
|
||||
|
||||
if (stat(path, &stat_buf) < 0)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
#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)
|
||||
#define path_mkdir_error(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST))
|
||||
#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(ORBIS)
|
||||
#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH)
|
||||
#define path_mkdir_error(ret) ((ret) == -1)
|
||||
#else
|
||||
#define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST)
|
||||
@ -1058,13 +982,11 @@ int retro_vfs_mkdir_impl(const char *dir)
|
||||
#endif
|
||||
#elif defined(IOS)
|
||||
int ret = mkdir(dir, 0755);
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
int ret = sceIoMkdir(dir, 0777);
|
||||
#elif defined(ORBIS)
|
||||
int ret = orbisMkdir(dir, 0755);
|
||||
#elif defined(__QNX__)
|
||||
int ret = mkdir(dir, 0777);
|
||||
#elif defined(GEKKO)
|
||||
#elif defined(GEKKO) || defined(WIIU)
|
||||
/* On GEKKO platforms, mkdir() fails if
|
||||
* the path has a trailing slash. We must
|
||||
* therefore remove it. */
|
||||
@ -1111,16 +1033,13 @@ struct libretro_vfs_implementation_dir
|
||||
HANDLE directory;
|
||||
bool next;
|
||||
char path[PATH_MAX_LENGTH];
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
SceUID directory;
|
||||
SceIoDirent entry;
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
int error;
|
||||
int directory;
|
||||
sysFSDirent entry;
|
||||
#elif defined(ORBIS)
|
||||
int directory;
|
||||
struct dirent entry;
|
||||
#else
|
||||
DIR *directory;
|
||||
const struct dirent *entry;
|
||||
@ -1131,7 +1050,7 @@ static bool dirent_check_error(libretro_vfs_implementation_dir *rdir)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return (rdir->directory == INVALID_HANDLE_VALUE);
|
||||
#elif defined(VITA) || defined(PSP) || defined(ORBIS)
|
||||
#elif defined(VITA) || defined(ORBIS)
|
||||
return (rdir->directory < 0);
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
return (rdir->error != FS_SUCCEEDED);
|
||||
@ -1144,7 +1063,6 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
|
||||
const char *name, bool include_hidden)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
unsigned path_len;
|
||||
char path_buf[1024];
|
||||
size_t copied = 0;
|
||||
#if defined(LEGACY_WIN32)
|
||||
@ -1155,28 +1073,25 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
|
||||
#endif
|
||||
libretro_vfs_implementation_dir *rdir;
|
||||
|
||||
/*Reject null or empty string paths*/
|
||||
/* Reject NULL or empty string paths*/
|
||||
if (!name || (*name == 0))
|
||||
return NULL;
|
||||
|
||||
/*Allocate RDIR struct. Tidied later with retro_closedir*/
|
||||
rdir = (libretro_vfs_implementation_dir*)calloc(1, sizeof(*rdir));
|
||||
if (!rdir)
|
||||
if (!(rdir = (libretro_vfs_implementation_dir*)
|
||||
calloc(1, sizeof(*rdir))))
|
||||
return NULL;
|
||||
|
||||
rdir->orig_path = strdup(name);
|
||||
|
||||
#if defined(_WIN32)
|
||||
path_buf[0] = '\0';
|
||||
path_len = strlen(name);
|
||||
|
||||
copied = strlcpy(path_buf, name, sizeof(path_buf));
|
||||
|
||||
/* Non-NT platforms don't like extra slashes in the path */
|
||||
if (name[path_len - 1] != '\\')
|
||||
path_buf[copied++] = '\\';
|
||||
if (path_buf[copied - 1] != '\\')
|
||||
path_buf [copied++] = '\\';
|
||||
|
||||
path_buf[copied] = '*';
|
||||
path_buf[copied ] = '*';
|
||||
path_buf[copied+1] = '\0';
|
||||
|
||||
#if defined(LEGACY_WIN32)
|
||||
@ -1193,15 +1108,13 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
|
||||
free(path_wide);
|
||||
#endif
|
||||
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
rdir->directory = sceIoDopen(name);
|
||||
#elif defined(_3DS)
|
||||
rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
|
||||
rdir->entry = NULL;
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
rdir->error = sysFsOpendir(name, &rdir->directory);
|
||||
#elif defined(ORBIS)
|
||||
rdir->directory = orbisDopen(name);
|
||||
#else
|
||||
rdir->directory = opendir(name);
|
||||
rdir->entry = NULL;
|
||||
@ -1233,14 +1146,12 @@ bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir)
|
||||
|
||||
rdir->next = true;
|
||||
return (rdir->directory != INVALID_HANDLE_VALUE);
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
return (sceIoDread(rdir->directory, &rdir->entry) > 0);
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
uint64_t nread;
|
||||
rdir->error = sysFsReaddir(rdir->directory, &rdir->entry, &nread);
|
||||
return (nread != 0);
|
||||
#elif defined(ORBIS)
|
||||
return (orbisDread(rdir->directory, &rdir->entry) > 0);
|
||||
#else
|
||||
return ((rdir->entry = readdir(rdir->directory)) != NULL);
|
||||
#endif
|
||||
@ -1259,7 +1170,7 @@ const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir
|
||||
if (name)
|
||||
free(name);
|
||||
return (char*)rdir->entry.cFileName;
|
||||
#elif defined(VITA) || defined(PSP) || defined(ORBIS) || defined(__PSL1GHT__) || defined(__PS3__)
|
||||
#elif defined(VITA) || defined(__PSL1GHT__) || defined(__PS3__)
|
||||
return rdir->entry.d_name;
|
||||
#else
|
||||
if (!rdir || !rdir->entry)
|
||||
@ -1273,22 +1184,12 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
|
||||
#if defined(_WIN32)
|
||||
const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
|
||||
return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||
#elif defined(PSP) || defined(VITA)
|
||||
const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
|
||||
#if defined(PSP)
|
||||
return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR;
|
||||
#elif defined(VITA)
|
||||
const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
|
||||
return SCE_S_ISDIR(entry->d_stat.st_mode);
|
||||
#endif
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
sysFSDirent *entry = (sysFSDirent*)&rdir->entry;
|
||||
return (entry->d_type == FS_TYPE_DIR);
|
||||
#elif defined(ORBIS)
|
||||
const struct dirent *entry = &rdir->entry;
|
||||
if (entry->d_type == DT_DIR)
|
||||
return true;
|
||||
if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
|
||||
return false;
|
||||
#else
|
||||
struct stat buf;
|
||||
char path[PATH_MAX_LENGTH];
|
||||
@ -1301,8 +1202,7 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
|
||||
return false;
|
||||
#endif
|
||||
/* dirent struct doesn't have d_type, do it the slow way ... */
|
||||
path[0] = '\0';
|
||||
fill_pathname_join(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
|
||||
fill_pathname_join_special(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
|
||||
if (stat(path, &buf) < 0)
|
||||
return false;
|
||||
return S_ISDIR(buf.st_mode);
|
||||
@ -1317,12 +1217,10 @@ int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir)
|
||||
#if defined(_WIN32)
|
||||
if (rdir->directory != INVALID_HANDLE_VALUE)
|
||||
FindClose(rdir->directory);
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
sceIoDclose(rdir->directory);
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
rdir->error = sysFsClosedir(rdir->directory);
|
||||
#elif defined(ORBIS)
|
||||
orbisDclose(rdir->directory);
|
||||
#else
|
||||
if (rdir->directory)
|
||||
closedir(rdir->directory);
|
||||
|
Loading…
Reference in New Issue
Block a user