libretro, add Pico pad overlay and storyware pages handling
authorkub <derkub@gmail.com>
Mon, 25 Mar 2024 17:49:19 +0000 (18:49 +0100)
committerkub <derkub@gmail.com>
Mon, 25 Mar 2024 23:30:53 +0000 (00:30 +0100)
17 files changed:
Makefile
Makefile.libretro
jni/Android.mk
platform/libretro/libretro-common/file/file_path.c
platform/libretro/libretro-common/file/file_path_io.c [new file with mode: 0644]
platform/libretro/libretro-common/formats/png/rpng.c [new file with mode: 0644]
platform/libretro/libretro-common/formats/png/rpng_internal.h [new file with mode: 0644]
platform/libretro/libretro-common/include/file/file_path.h
platform/libretro/libretro-common/include/filters.h [new file with mode: 0644]
platform/libretro/libretro-common/include/formats/image.h [new file with mode: 0644]
platform/libretro/libretro-common/include/formats/rpng.h [new file with mode: 0644]
platform/libretro/libretro-common/include/retro_math.h [new file with mode: 0644]
platform/libretro/libretro-common/include/streams/trans_stream.h [new file with mode: 0644]
platform/libretro/libretro-common/streams/trans_stream.c [new file with mode: 0644]
platform/libretro/libretro-common/streams/trans_stream_pipe.c [new file with mode: 0644]
platform/libretro/libretro-common/streams/trans_stream_zlib.c [new file with mode: 0644]
platform/libretro/libretro.c

index 3fe87cb..7b87043 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -72,8 +72,10 @@ CFLAGS += $(call chkCCflag, -fno-caller-saves -fno-guess-branch-probability -fno
 CFLAGS += $(call chkCCflag, -fno-tree-loop-if-convert -fipa-pta -fno-ipa-cp)
 endif
 else
+ifneq ($(STATIC_LINKING), 1)
 CFLAGS += $(call chkCCflag, -flto)
 endif
+endif
 
 # revision info from repository if this not a tagged release
 ifeq "$(shell git describe --tags --exact-match HEAD 2>/dev/null)" ""
index d849be8..9a2e2eb 100644 (file)
@@ -30,6 +30,7 @@ CFLAGS ?=
 CFLAGS += -I platform/libretro/libretro-common/include
 CFLAGS += -I platform/libretro/libretro-common/include/compat
 CFLAGS += -I platform/libretro/libretro-common/include/encodings
+CFLAGS += -I platform/libretro/libretro-common/include/formats
 CFLAGS += -I platform/libretro/libretro-common/include/streams
 CFLAGS += -I platform/libretro/libretro-common/include/string
 CFLAGS += -I platform/libretro/libretro-common/include/vfs
index f89e132..b4d0143 100644 (file)
@@ -54,7 +54,18 @@ endif
 include $(COMMON_DIR)/common.mak
 
 SOURCES_C := $(LIBRETRO_DIR)/libretro.c \
+             $(LIBRETRO_COMM_DIR)/formats/png/rpng.c \
+             $(LIBRETRO_COMM_DIR)/streams/trans_stream.c \
+             $(LIBRETRO_COMM_DIR)/streams/trans_stream_pipe.c \
+             $(LIBRETRO_COMM_DIR)/streams/trans_stream_zlib.c \
+             $(LIBRETRO_COMM_DIR)/file/file_path_io.c \
+             $(LIBRETRO_COMM_DIR)/file/file_path.c \
+             $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
+             $(LIBRETRO_COMM_DIR)/time/rtime.c \
+             $(LIBRETRO_COMM_DIR)/string/stdstring.c \
+             $(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
              $(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \
+             $(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
              $(COMMON_DIR)/mp3.c \
              $(COMMON_DIR)/mp3_sync.c \
              $(COMMON_DIR)/mp3_dummy.c \
index 320c5a2..c696ff0 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
-#include <errno.h>
+#include <locale.h>
 
 #include <sys/stat.h>
 
 #include <boolean.h>
 #include <file/file_path.h>
-#include <retro_assert.h>
 #include <retro_miscellaneous.h>
 #include <string/stdstring.h>
 #include <time/rtime.h>
 
 #endif
 
+/* Time format strings with AM-PM designation require special
+ * handling due to platform dependence */
+void strftime_am_pm(char *s, size_t len, const char* format,
+      const void *ptr)
+{
+   char *local = NULL;
+   const struct tm *timeptr = (const struct tm*)ptr;
+
+   /* Ensure correct locale is set
+    * > Required for localised AM/PM strings */
+   setlocale(LC_TIME, "");
+
+   strftime(s, len, format, timeptr);
+#if !(defined(__linux__) && !defined(ANDROID))
+   if ((local = local_to_utf8_string_alloc(s)))
+   {
+      if (!string_is_empty(local))
+         strlcpy(s, local, len);
+
+      free(local);
+      local = NULL;
+   }
+#endif
+}
+
+/**
+ * Create a new linked list with one node in it
+ * The path on this node will be set to NULL
+**/
+struct path_linked_list* path_linked_list_new(void)
+{
+   struct path_linked_list* paths_list = (struct path_linked_list*)malloc(sizeof(*paths_list));
+   paths_list->next = NULL;
+   paths_list->path = NULL;
+   return paths_list;
+}
+
+/**
+ * path_linked_list_free:
+ *
+ * Free the entire linked list
+ **/
+void path_linked_list_free(struct path_linked_list *in_path_linked_list)
+{
+   struct path_linked_list *node_tmp = (struct path_linked_list*)in_path_linked_list;
+   while (node_tmp)
+   {
+      struct path_linked_list *hold = NULL;
+      if (node_tmp->path)
+         free(node_tmp->path);
+      hold     = (struct path_linked_list*)node_tmp;
+      node_tmp = node_tmp->next;
+      if (hold)
+         free(hold);
+   }
+}
+
+/**
+ * path_linked_list_add_path:
+ *
+ * Add a node to the linked list with this path
+ * If the first node's path if it's not yet set the path
+ * on this node instead
+**/
+void path_linked_list_add_path(struct path_linked_list *in_path_linked_list,
+      char *path)
+{
+    /* If the first item does not have a path this is
+      a list which has just been created, so we just fill 
+      the path for the first item
+   */
+   if (!in_path_linked_list->path)
+      in_path_linked_list->path = strdup(path);
+   else
+   {  
+      struct path_linked_list *node = (struct path_linked_list*) malloc(sizeof(*node));
+
+      if (node)
+      {
+         struct path_linked_list *head = in_path_linked_list;
+
+         node->next                    = NULL;
+         node->path                    = strdup(path);
+
+         if (head)
+         {
+            while (head->next)
+               head        = head->next;
+
+            head->next     = node;
+         }
+         else
+            in_path_linked_list = node;
+      }
+   }
+}
+
 /**
  * path_get_archive_delim:
  * @path               : path
  * 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
+ * @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)
 {
-   const char *delim      = NULL;
    char buf[5];
-
-   buf[0] = '\0';
-
    /* Find delimiter position
     * > Since filenames may contain '#' characters,
     *   must loop until we find the first '#' that
     *   is directly *after* a compression extension */
-   delim = strchr(path, '#');
+   const char *delim      = strchr(path, '#');
 
    while (delim)
    {
@@ -106,9 +198,9 @@ const char *path_get_archive_delim(const char *path)
          string_to_lower(buf);
 
          /* Check if this is a '.zip', '.apk' or '.7z' file */
-         if (string_is_equal(buf,     ".zip") ||
-             string_is_equal(buf,     ".apk") ||
-             string_is_equal(buf + 1, ".7z"))
+         if (   string_is_equal(buf,     ".zip")
+             || string_is_equal(buf,     ".apk")
+             || string_is_equal(buf + 1, ".7z"))
             return delim;
       }
       else if (delim - path > 3)
@@ -137,16 +229,36 @@ 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.
- */
+ * @return extension part from the path.
+ **/
 const char *path_get_extension(const char *path)
 {
    const char *ext;
-   if (!string_is_empty(path) && ((ext = strrchr(path_basename(path), '.'))))
+   if (!string_is_empty(path) && ((ext = (char*)strrchr(path_basename(path), '.'))))
       return ext + 1;
    return "";
 }
 
+/**
+ * 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)
+{
+   char *ext = NULL;
+   if (!string_is_empty(path) && ((ext = (char*)strrchr(path_basename(path), '.'))))
+      return ext;
+   return NULL;
+}
+
 /**
  * path_remove_extension:
  * @path               : path
@@ -155,12 +267,12 @@ const char *path_get_extension(const char *path)
  * text after and including the last '.'.
  * Only '.'s after the last slash are considered.
  *
- * Returns:
+ * @return
  * 1) If path has an extension, returns path with the
  *    extension removed.
  * 2) If there is no extension, returns NULL.
  * 3) If path is empty or NULL, returns NULL
- */
+ **/
 char *path_remove_extension(char *path)
 {
    char *last = !string_is_empty(path)
@@ -178,16 +290,15 @@ char *path_remove_extension(char *path)
  *
  * Checks if path is a compressed file.
  *
- * Returns: true (1) if path is a compressed file, otherwise false (0).
+ * @return true if path is a compressed file, otherwise false.
  **/
 bool path_is_compressed_file(const char* path)
 {
    const char *ext = path_get_extension(path);
    if (!string_is_empty(ext))
-      if (  string_is_equal_noncase(ext, "zip") ||
-            string_is_equal_noncase(ext, "apk") ||
-            string_is_equal_noncase(ext, "7z"))
-         return true;
+      return (   string_is_equal_noncase(ext, "zip")
+              || string_is_equal_noncase(ext, "apk")
+              || string_is_equal_noncase(ext, "7z"));
    return false;
 }
 
@@ -211,53 +322,45 @@ bool path_is_compressed_file(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"
+ *
+ * @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)
 {
+   size_t _len;
    char tmp_path[PATH_MAX_LENGTH];
-   char *tok                      = NULL;
-
-   tmp_path[0] = '\0';
-
+   char *tok   = NULL;
    strlcpy(tmp_path, in_path, sizeof(tmp_path));
    if ((tok = (char*)strrchr(path_basename(tmp_path), '.')))
       *tok = '\0';
 
-   fill_pathname_noext(out_path, tmp_path, replace, size);
+   _len  = strlcpy(out_path, tmp_path, size);
+   _len += strlcpy(out_path + _len, replace, size - _len);
+   return _len;
 }
 
+
 /**
- * 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.
+ * find_last_slash:
+ * @str                : path
+ * @size               : size of path
  *
- */
-size_t fill_pathname_noext(char *out_path, const char *in_path,
-      const char *replace, size_t size)
-{
-   strlcpy(out_path, in_path, size);
-   return strlcat(out_path, replace, size);
-}
+ * Find last slash in path. Tries to find
+ * a backslash as used for Windows paths,
+ * otherwise checks for a regular slash.
 
+ * @return pointer to last slash/backslash found in @str.
+ **/
 char *find_last_slash(const char *str)
 {
    const char *slash     = strrchr(str, '/');
-#ifdef _WIN32
    const char *backslash = strrchr(str, '\\');
 
    if (!slash || (backslash > slash))
       return (char*)backslash;
-#endif
-   return (char*)slash;
+   else
+      return (char*)slash;
 }
 
 /**
@@ -268,24 +371,20 @@ char *find_last_slash(const char *str)
  * Assumes path is a directory. Appends a slash
  * if not already there.
  **/
-void fill_pathname_slash(char *path, size_t size)
+size_t fill_pathname_slash(char *path, size_t size)
 {
    size_t path_len;
    const char *last_slash = find_last_slash(path);
-
    if (!last_slash)
-   {
-      strlcat(path, PATH_DEFAULT_SLASH(), size);
-      return;
-   }
-
-   path_len               = strlen(path);
+      return strlcat(path, PATH_DEFAULT_SLASH(), size);
+   path_len            = strlen(path);
    /* Try to preserve slash type. */
    if (last_slash != (path + path_len - 1))
    {
-      path[path_len]   = last_slash[0];
-      path[path_len+1] = '\0';
+      path[  path_len] = last_slash[0];
+      path[++path_len] = '\0';
    }
+   return path_len;
 }
 
 /**
@@ -308,12 +407,11 @@ void fill_pathname_slash(char *path, size_t size)
 size_t fill_pathname_dir(char *in_dir, const char *in_basename,
       const char *replace, size_t size)
 {
-   const char *base = NULL;
-
-   fill_pathname_slash(in_dir, size);
-   base = path_basename(in_basename);
-   strlcat(in_dir, base, size);
-   return strlcat(in_dir, replace, size);
+   size_t _len      = fill_pathname_slash(in_dir, size);
+   const char *base = path_basename(in_basename);
+   _len            += strlcpy(in_dir + _len, base,    size - _len);
+   _len            += strlcpy(in_dir + _len, replace, size - _len);
+   return _len;
 }
 
 /**
@@ -323,30 +421,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.
+ *
+ * @return Length of the string copied into @out
  **/
 size_t fill_pathname_base(char *out, const char *in_path, size_t size)
 {
    const char     *ptr = path_basename(in_path);
-
-   if (!ptr)
-      ptr = in_path;
-
-   return strlcpy(out, ptr, size);
-}
-
-void fill_pathname_base_noext(char *out,
-      const char *in_path, size_t size)
-{
-   fill_pathname_base(out, in_path, size);
-   path_remove_extension(out);
-}
-
-size_t fill_pathname_base_ext(char *out,
-      const char *in_path, const char *ext,
-      size_t size)
-{
-   fill_pathname_base_noext(out, in_path, size);
-   return strlcat(out, ext, size);
+   if (ptr)
+      return strlcpy(out, ptr, size);
+   return strlcpy(out, in_path, size);
 }
 
 /**
@@ -367,13 +450,6 @@ void fill_pathname_basedir(char *out_dir,
    path_basedir(out_dir);
 }
 
-void fill_pathname_basedir_noext(char *out_dir,
-      const char *in_path, size_t size)
-{
-   fill_pathname_basedir(out_dir, in_path, size);
-   path_remove_extension(out_dir);
-}
-
 /**
  * fill_pathname_parent_dir_name:
  * @out_dir            : output directory
@@ -382,12 +458,12 @@ 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.
+ *
+ * @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)
 {
-   bool success = false;
    char *temp   = strdup(in_dir);
    char *last   = find_last_slash(temp);
 
@@ -403,26 +479,24 @@ bool fill_pathname_parent_dir_name(char *out_dir,
       *last     = '\0';
 
    /* Point in_dir to the address of the last slash. */
-   in_dir       = find_last_slash(temp);
-
    /* If find_last_slash returns NULL, it means there was no slash in temp,
       so use temp as-is. */
-   if (!in_dir)
+   if (!(in_dir = find_last_slash(temp)))
        in_dir   = temp;
 
-   success      = in_dir && in_dir[1];
-
-   if (success)
+   if (in_dir && in_dir[1])
    {
        /* If path starts with an slash, eliminate it. */
        if (path_is_absolute(in_dir))
            strlcpy(out_dir, in_dir + 1, size);
        else
            strlcpy(out_dir, in_dir, size);
+       free(temp);
+       return true;
    }
 
    free(temp);
-   return success;
+   return false;
 }
 
 /**
@@ -433,14 +507,18 @@ 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.
+ * If the path was already at the root directory,
+ * @out_dir will be an empty string.
  **/
 void fill_pathname_parent_dir(char *out_dir,
       const char *in_dir, size_t size)
 {
+   size_t len = 0;
    if (out_dir != in_dir)
-      strlcpy(out_dir, in_dir, size);
-   path_parent_dir(out_dir);
+      len = strlcpy(out_dir, in_dir, size);
+   else
+      len = strlen(out_dir);
+   path_parent_dir(out_dir, len);
 }
 
 /**
@@ -458,14 +536,14 @@ void fill_pathname_parent_dir(char *out_dir,
 size_t fill_dated_filename(char *out_filename,
       const char *ext, size_t size)
 {
-   time_t cur_time = time(NULL);
+   size_t _len;
    struct tm tm_;
-
+   time_t cur_time = time(NULL);
    rtime_localtime(&cur_time, &tm_);
-
-   strftime(out_filename, size,
+   _len  = strftime(out_filename, size,
          "RetroArch-%m%d-%H%M%S", &tm_);
-   return strlcat(out_filename, ext, size);
+   _len += strlcpy(out_filename + _len, ext, size - _len);
+   return _len;
 }
 
 /**
@@ -480,31 +558,30 @@ size_t fill_dated_filename(char *out_filename,
  *
  * E.g.:
  * out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
+ *
+ * @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)
 {
-   char format[256];
    struct tm tm_;
+   char format[NAME_MAX_LENGTH];
+   size_t _len     = 0;
    time_t cur_time = time(NULL);
-
-   format[0]       = '\0';
-
    rtime_localtime(&cur_time, &tm_);
-
+   _len      = strlcpy(out_filename, in_str, size);
    if (string_is_empty(ext))
    {
       strftime(format, sizeof(format), "-%y%m%d-%H%M%S", &tm_);
-      fill_pathname_noext(out_filename, in_str, format, size);
+      _len  += strlcpy(out_filename + _len, format, size - _len);
    }
    else
    {
       strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", &tm_);
-
-      fill_pathname_join_concat_noext(out_filename,
-            in_str, format, ext,
-            size);
+      _len  += strlcpy(out_filename + _len, format, size - _len);
+      _len  += strlcpy(out_filename + _len, ext,    size - _len);
    }
+   return _len;
 }
 
 /**
@@ -517,35 +594,33 @@ void fill_str_dated_filename(char *out_filename,
 void path_basedir(char *path)
 {
    char *last = NULL;
-
-   if (strlen(path) < 2)
+   if (!path || path[0] == '\0' || path[1] == '\0')
       return;
 
-   last = find_last_slash(path);
-
-   if (last)
+   if ((last = find_last_slash(path)))
       last[1] = '\0';
    else
-      strlcpy(path, "." PATH_DEFAULT_SLASH(), 3);
+   {
+      path[0] = '.';
+      path[1] = PATH_DEFAULT_SLASH_C();
+      path[2] = '\0';
+   }
 }
 
 /**
  * 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)
 {
-   size_t len = 0;
-
    if (!path)
       return;
    
-   len = strlen(path);
-
    if (len && PATH_CHAR_IS_SLASH(path[len - 1]))
    {
       bool path_was_absolute = path_is_absolute(path);
@@ -572,26 +647,29 @@ void path_parent_dir(char *path)
  *
  * Get basename from @path.
  *
- * Returns: basename from path.
+ * @return basename from path.
  **/
 const char *path_basename(const char *path)
 {
-   /* We cut at the first compression-related hash */
-   const char *delim = path_get_archive_delim(path);
-   if (delim)
-      return delim + 1;
-
-   {
-      /* We cut at the last slash */
-      const char *last  = find_last_slash(path);
-      if (last)
-         return last + 1;
-   }
-
+   /* We cut either at the first compression-related hash,
+    * or we cut at the last slash */
+   const char *ptr = NULL;
+   if (     (ptr = path_get_archive_delim(path))
+         || (ptr = find_last_slash(path)))
+      return ptr + 1;
    return path;
 }
 
 /* Specialized version */
+/**
+ * path_basename_nocompression:
+ * @path               : path
+ *
+ * Specialized version of path_basename().
+ * Get basename from @path.
+ *
+ * @return basename from path.
+ **/
 const char *path_basename_nocompression(const char *path)
 {
    /* We cut at the last slash */
@@ -607,30 +685,27 @@ const char *path_basename_nocompression(const char *path)
  *
  * 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)
 {
-   if (string_is_empty(path))
-      return false;
-
-   if (path[0] == '/')
-      return true;
-
-#if defined(_WIN32)
-   /* Many roads lead to Rome...
-    * Note: Drive letter can only be 1 character long */
-   if (string_starts_with_size(path,     "\\\\", STRLEN_CONST("\\\\")) ||
-       string_starts_with_size(path + 1, ":/",   STRLEN_CONST(":/"))   ||
-       string_starts_with_size(path + 1, ":\\",  STRLEN_CONST(":\\")))
-      return true;
-#elif defined(__wiiu__) || defined(VITA)
+   if (!string_is_empty(path))
    {
-      const char *seperator = strchr(path, ':');
-      if (seperator && (seperator[1] == '/'))
+      if (path[0] == '/')
          return true;
-   }
+#if defined(_WIN32)
+      /* Many roads lead to Rome...
+       * Note: Drive letter can only be 1 character long */
+      return ( string_starts_with_size(path,     "\\\\", STRLEN_CONST("\\\\"))
+            || string_starts_with_size(path + 1, ":/",   STRLEN_CONST(":/")) 
+            || string_starts_with_size(path + 1, ":\\",  STRLEN_CONST(":\\")));
+#elif defined(__wiiu__) || defined(VITA)
+      {
+         const char *seperator = strchr(path, ':');
+         return (seperator && (seperator[1] == '/'));
+      }
 #endif
+   }
 
    return false;
 }
@@ -645,7 +720,7 @@ bool path_is_absolute(const 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,
@@ -656,12 +731,13 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
 {
 #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
 #ifdef _WIN32
-   char *ret = NULL;
-   wchar_t abs_path[PATH_MAX_LENGTH];
+   char *ret         = NULL;
    wchar_t *rel_path = utf8_to_utf16_string_alloc(buf);
 
    if (rel_path)
    {
+      wchar_t abs_path[PATH_MAX_LENGTH];
+
       if (_wfullpath(abs_path, rel_path, PATH_MAX_LENGTH))
       {
          char *tmp = utf16_to_utf8_string_alloc(abs_path);
@@ -702,20 +778,20 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
       return buf;
    }
 
-   t = 0; /* length of output */
+   t       = 0; /* length of output */
    buf_end = buf + strlen(buf);
 
    if (!path_is_absolute(buf))
    {
       size_t len;
       /* rebase on working directory */
-      if (!getcwd(tmp, PATH_MAX_LENGTH-1))
+      if (!getcwd(tmp, PATH_MAX_LENGTH - 1))
          return NULL;
 
       len = strlen(tmp);
-      t += len;
+      t  += len;
 
-      if (tmp[len-1] != '/')
+      if (tmp[len - 1] != '/')
          tmp[t++] = '/';
 
       if (string_is_empty(buf))
@@ -734,8 +810,7 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
     * if there are no slashes, they point relative to where one would be */
    do
    {
-      next = strchr(p, '/');
-      if (!next)
+      if (!(next = strchr(p, '/')))
          next = buf_end;
 
       if ((next - p == 2 && p[0] == '.' && p[1] == '.'))
@@ -747,8 +822,8 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
             return NULL;
 
          /* delete previous segment in tmp by adjusting size t
-          * tmp[t-1] == '/', find '/' before that */
-         t = t-2;
+          * tmp[t - 1] == '/', find '/' before that */
+         t -= 2;
          while (tmp[t] != '/')
             t--;
          t++;
@@ -760,15 +835,14 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
       else
       {
          /* fail when truncating */
-         if (t + next-p+1 > PATH_MAX_LENGTH-1)
+         if (t + next - p + 1 > PATH_MAX_LENGTH - 1)
             return NULL;
 
          while (p <= next)
             tmp[t++] = *p++;
       }
-
-   }
-   while (next < buf_end);
+   }while(next < buf_end);
+   
 
 end:
    tmp[t] = '\0';
@@ -792,6 +866,8 @@ end:
  * Both @path and @base are assumed to be absolute paths without "." or "..".
  *
  * E.g. path /a/b/e/f.cg with base /a/b/c/d/ turns into ../../e/f.cg
+ *
+ * @return Length of the string copied into @out
  **/
 size_t path_relative_to(char *out,
       const char *path, const char *base, size_t size)
@@ -801,8 +877,15 @@ size_t path_relative_to(char *out,
 
 #ifdef _WIN32
    /* For different drives, return absolute path */
-   if (strlen(path) >= 2 && strlen(base) >= 2
-         && path[1] == ':' && base[1] == ':'
+   if (
+            path
+         && base
+         && path[0] != '\0' 
+         && path[1] != '\0'
+         && base[0] != '\0'
+         && base[1] != '\0'
+         && path[1] == ':' 
+         && base[1] == ':'
          && path[0] != base[0])
       return strlcpy(out, path, size);
 #endif
@@ -845,7 +928,9 @@ void fill_pathname_resolve_relative(char *out_path,
       return;
    }
 
-   fill_pathname_basedir(out_path, in_refpath, size);
+   if (out_path != in_refpath)
+      strlcpy(out_path, in_refpath, size);
+   path_basedir(out_path);
    strlcat(out_path, in_path, size);
    path_resolve_realpath(out_path, size, false);
 }
@@ -860,17 +945,68 @@ void fill_pathname_resolve_relative(char *out_path,
  * Joins a directory (@dir) and path (@path) together.
  * Makes sure not to get  two consecutive slashes
  * between directory and path.
+ *
+ * Deprecated. Use fill_pathname_join_special() instead
+ * if you can ensure @dir and @out_path won't overlap.
+ *
+ * @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)
 {
+   size_t _len = 0;
    if (out_path != dir)
-      strlcpy(out_path, dir, size);
+      _len = strlcpy(out_path, dir, size);
+   if (*out_path)
+      _len = fill_pathname_slash(out_path, size);
+   _len   += strlcpy(out_path + _len, path, size - _len);
+   return _len;
+}
+
+/**
+ * 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.
+ *
+ * @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 len = strlcpy(out_path, dir, size);
 
    if (*out_path)
-      fill_pathname_slash(out_path, size);
+   {
+      const char *last_slash = find_last_slash(out_path);
+      if (last_slash)
+      {
+         /* Try to preserve slash type. */
+         if (last_slash != (out_path + len - 1))
+         {
+            out_path[  len] = last_slash[0];
+            out_path[++len] = '\0';
+         }
+      }
+      else
+      {
+         out_path[  len]    = PATH_DEFAULT_SLASH_C();
+         out_path[++len]    = '\0';
+      }
+   }
 
-   return strlcat(out_path, path, size);
+   len += strlcpy(out_path + len, path, size - len);
+   return len;
 }
 
 size_t fill_pathname_join_special_ext(char *out_path,
@@ -878,37 +1014,12 @@ size_t fill_pathname_join_special_ext(char *out_path,
       const char *last, const char *ext,
       size_t size)
 {
-   fill_pathname_join(out_path, dir, path, size);
+   size_t _len = fill_pathname_join(out_path, dir, path, size);
    if (*out_path)
-      fill_pathname_slash(out_path, size);
-
-   strlcat(out_path, last, size);
-   return strlcat(out_path, ext, size);
-}
-
-size_t fill_pathname_join_concat_noext(char *out_path,
-      const char *dir, const char *path,
-      const char *concat,
-      size_t size)
-{
-   fill_pathname_noext(out_path, dir, path, size);
-   return strlcat(out_path, concat, size);
-}
-
-size_t fill_pathname_join_concat(char *out_path,
-      const char *dir, const char *path,
-      const char *concat,
-      size_t size)
-{
-   fill_pathname_join(out_path, dir, path, size);
-   return strlcat(out_path, concat, size);
-}
-
-void fill_pathname_join_noext(char *out_path,
-      const char *dir, const char *path, size_t size)
-{
-   fill_pathname_join(out_path, dir, path, size);
-   path_remove_extension(out_path);
+      _len     = fill_pathname_slash(out_path, size);
+   _len       += strlcpy(out_path + _len, last, size - _len);
+   _len       += strlcpy(out_path + _len, ext,  size - _len);
+   return _len;
 }
 
 /**
@@ -925,120 +1036,50 @@ void fill_pathname_join_noext(char *out_path,
 size_t fill_pathname_join_delim(char *out_path, const char *dir,
       const char *path, const char delim, size_t size)
 {
-   size_t copied;
-   /* behavior of strlcpy is undefined if dst and src overlap */
+   size_t _len;
+   /* Behavior of strlcpy is undefined if dst and src overlap */
    if (out_path == dir)
-      copied = strlen(dir);
+      _len          = strlen(dir);
    else
-      copied = strlcpy(out_path, dir, size);
+      _len          = strlcpy(out_path, dir, size);
 
-   out_path[copied]   = delim;
-   out_path[copied+1] = '\0';
+   out_path[_len]   = delim;
+   out_path[_len+1] = '\0';
 
    if (path)
-      copied = strlcat(out_path, path, size);
-   return copied;
-}
-
-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)
-{
-   fill_pathname_join_delim(out_path, dir, path, delim, size);
-   return strlcat(out_path, concat, size);
-}
-
-/**
- * fill_short_pathname_representation:
- * @out_rep            : output representation
- * @in_path            : input path
- * @size               : size of output representation
- *
- * 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.
- *
- * 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)
-{
-   char path_short[NAME_MAX_LENGTH];
-
-   path_short[0] = '\0';
-
-   fill_pathname(path_short, path_basename(in_path), "",
-            sizeof(path_short));
-
-   return strlcpy(out_rep, path_short, size);
+      return strlcat(out_path, path, size);
+   return _len;
 }
 
-void fill_short_pathname_representation_noext(char* out_rep,
-      const char *in_path, size_t size)
-{
-   fill_short_pathname_representation(out_rep, in_path, size);
-   path_remove_extension(out_rep);
-}
-
-void fill_pathname_expand_special(char *out_path,
+size_t fill_pathname_expand_special(char *out_path,
       const char *in_path, size_t size)
 {
 #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
+   char *app_dir = NULL;
    if (in_path[0] == '~')
    {
-      char *home_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
-
-      home_dir[0] = '\0';
-
-      fill_pathname_home_dir(home_dir,
-         PATH_MAX_LENGTH * sizeof(char));
-
-      if (*home_dir)
-      {
-         size_t src_size = strlcpy(out_path, home_dir, size);
-         retro_assert(src_size < size);
-
-         out_path  += src_size;
-         size      -= src_size;
-
-         if (!PATH_CHAR_IS_SLASH(out_path[-1]))
-         {
-            src_size = strlcpy(out_path, PATH_DEFAULT_SLASH(), size);
-            retro_assert(src_size < size);
-
-            out_path += src_size;
-            size     -= src_size;
-         }
-
-         in_path += 2;
-      }
-
-      free(home_dir);
+      app_dir    = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
+      fill_pathname_home_dir(app_dir, PATH_MAX_LENGTH * sizeof(char));
    }
    else if (in_path[0] == ':')
    {
-      char *application_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
-
-      application_dir[0]    = '\0';
-
-      fill_pathname_application_dir(application_dir,
-            PATH_MAX_LENGTH * sizeof(char));
+      app_dir    = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
+      app_dir[0] = '\0';
+      fill_pathname_application_dir(app_dir, PATH_MAX_LENGTH * sizeof(char));
+   }
 
-      if (*application_dir)
+   if (app_dir)
+   {
+      if (*app_dir)
       {
-         size_t src_size   = strlcpy(out_path, application_dir, size);
-         retro_assert(src_size < size);
+         size_t src_size = strlcpy(out_path, app_dir, size);
 
-         out_path  += src_size;
-         size      -= src_size;
+         out_path       += src_size;
+         size           -= src_size;
 
          if (!PATH_CHAR_IS_SLASH(out_path[-1]))
          {
-            src_size = strlcpy(out_path, PATH_DEFAULT_SLASH(), size);
-            retro_assert(src_size < size);
+            src_size  = strlcpy(out_path, PATH_DEFAULT_SLASH(), size);
 
             out_path += src_size;
             size     -= src_size;
@@ -1047,14 +1088,13 @@ void fill_pathname_expand_special(char *out_path,
          in_path += 2;
       }
 
-      free(application_dir);
+      free(app_dir);
    }
 #endif
-
-   retro_assert(strlcpy(out_path, in_path, size) < size);
+   return strlcpy(out_path, in_path, size);
 }
 
-void fill_pathname_abbreviate_special(char *out_path,
+size_t fill_pathname_abbreviate_special(char *out_path,
       const char *in_path, size_t size)
 {
 #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
@@ -1065,7 +1105,6 @@ void fill_pathname_abbreviate_special(char *out_path,
    char home_dir[PATH_MAX_LENGTH];
 
    application_dir[0] = '\0';
-   home_dir[0]        = '\0';
 
    /* application_dir could be zero-string. Safeguard against this.
     *
@@ -1092,8 +1131,6 @@ void fill_pathname_abbreviate_special(char *out_path,
       {
          size_t src_size  = strlcpy(out_path, notations[i], size);
 
-         retro_assert(src_size < size);
-
          out_path        += src_size;
          size            -= src_size;
          in_path         += strlen(candidates[i]);
@@ -1108,35 +1145,58 @@ void fill_pathname_abbreviate_special(char *out_path,
          break; /* Don't allow more abbrevs to take place. */
       }
    }
-
 #endif
-
-   retro_assert(strlcpy(out_path, in_path, size) < size);
+   return strlcpy(out_path, in_path, size);
 }
 
-/* Changes the slashes to the correct kind for the os 
- * So forward slash on linux and backslash on Windows */
+/**
+ * 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)
 {
-   /* Conform slashes to os standard so we get proper matching */
-   charp;
+   /* Conform slashes to OS standard so we get proper matching */
+   char *p;
    for (p = path; *p; p++)
       if (*p == '/' || *p == '\\')
          *p = PATH_DEFAULT_SLASH_C();
 }
 
-/* Change all shashes to forward so they are more portable between windows and linux */
+/**
+ * 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)
 {
-   /* Conform slashes to os standard so we get proper matching */
-   charp;
+   /* Conform slashes to OS standard so we get proper matching */
+   char *p;
    for (p = path; *p; p++)
       if (*p == '/' || *p == '\\')
          *p = '/';
 }
 
-/* Get the number of slashes in a path, returns an integer */
-int get_pathname_num_slashes(const char *in_path)
+/**
+ * get_pathname_num_slashes:
+ * @in_path            : input path
+ *
+ * Leaf function.
+ *
+ * Get the number of slashes in a path.
+ *
+ * @return number of slashes found in @in_path.
+ **/
+static int get_pathname_num_slashes(const char *in_path)
 {
    int num_slashes = 0;
    int i = 0;
@@ -1152,11 +1212,20 @@ int get_pathname_num_slashes(const char *in_path)
    return num_slashes;
 }
 
-/* Fills the supplied path with either the abbreviated path or the relative path, which ever
- * one is has less depth / number of slashes
- * 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 */
-void fill_pathname_abbreviated_or_relative(char *out_path, const char *in_refpath, const char *in_path, size_t size)
+/**
+ * fill_pathname_abbreviated_or_relative:
+ *
+ * Fills the supplied path with either the abbreviated path or 
+ * the relative path, which ever one has less depth / number of slashes
+ * 
+ * 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)
 {
    char in_path_conformed[PATH_MAX_LENGTH];
    char in_refpath_conformed[PATH_MAX_LENGTH];
@@ -1165,15 +1234,13 @@ void fill_pathname_abbreviated_or_relative(char *out_path, const char *in_refpat
    char relative_path[PATH_MAX_LENGTH];
    char abbreviated_path[PATH_MAX_LENGTH];
    
-   in_path_conformed[0]    = '\0';
-   in_refpath_conformed[0] = '\0';
    expanded_path[0]        = '\0';
    absolute_path[0]        = '\0';
    relative_path[0]        = '\0';
    abbreviated_path[0]     = '\0';
 
-   strcpy_literal(in_path_conformed, in_path);
-   strcpy_literal(in_refpath_conformed, in_refpath);
+   strlcpy(in_path_conformed, in_path, sizeof(in_path_conformed));
+   strlcpy(in_refpath_conformed, in_refpath, sizeof(in_refpath_conformed));
 
    pathname_conform_slashes_to_os(in_path_conformed);
    pathname_conform_slashes_to_os(in_refpath_conformed);
@@ -1202,9 +1269,8 @@ void fill_pathname_abbreviated_or_relative(char *out_path, const char *in_refpat
    /* Use the shortest path, preferring the relative path*/
    if (  get_pathname_num_slashes(relative_path) <= 
          get_pathname_num_slashes(abbreviated_path))
-      retro_assert(strlcpy(out_path, relative_path, size) < size);
-   else
-      retro_assert(strlcpy(out_path, abbreviated_path, size) < size);
+      return strlcpy(out_path, relative_path, size);
+   return strlcpy(out_path, abbreviated_path, size);
 }
 
 /**
@@ -1217,22 +1283,23 @@ void fill_pathname_abbreviated_or_relative(char *out_path, const char *in_refpat
 void path_basedir_wrapper(char *path)
 {
    char *last = NULL;
-   if (strlen(path) < 2)
+   if (!path || path[0] == '\0' || path[1] == '\0')
       return;
 
 #ifdef HAVE_COMPRESSION
    /* We want to find the directory with the archive in basedir. */
-   last = (char*)path_get_archive_delim(path);
-   if (last)
+   if ((last = (char*)path_get_archive_delim(path)))
       *last = '\0';
 #endif
 
-   last = find_last_slash(path);
-
-   if (last)
+   if ((last = find_last_slash(path)))
       last[1] = '\0';
    else
-      strlcpy(path, "." PATH_DEFAULT_SLASH(), 3);
+   {
+      path[0] = '.';
+      path[1] = PATH_DEFAULT_SLASH_C();
+      path[2] = '\0';
+   }
 }
 
 #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
@@ -1287,8 +1354,9 @@ void fill_pathname_application_path(char *s, size_t len)
          char resolved_bundle_dir_buf[PATH_MAX_LENGTH] = {0};
          if (realpath(s, resolved_bundle_dir_buf))
          {
-            strlcpy(s, resolved_bundle_dir_buf, len - 1);
-            strlcat(s, "/", len);
+            size_t _len = strlcpy(s, resolved_bundle_dir_buf, len - 1);
+            s[  _len]   = '/';
+            s[++_len]   = '\0';
          }
       }
 #endif
@@ -1298,7 +1366,7 @@ void fill_pathname_application_path(char *s, size_t len)
 #ifndef HAVE_COCOATOUCH
       /* Not sure what this does but it breaks 
        * stuff for iOS, so skipping */
-      retro_assert(strlcat(s, "nobin", len) < len);
+      strlcat(s, "nobin", len);
 #endif
       return;
    }
@@ -1334,9 +1402,8 @@ void fill_pathname_application_path(char *s, size_t len)
 
          snprintf(link_path, sizeof(link_path), "/proc/%u/%s",
                (unsigned)pid, exts[i]);
-         ret = readlink(link_path, s, len - 1);
 
-         if (ret >= 0)
+         if ((ret = readlink(link_path, s, len - 1)) >= 0)
          {
             s[ret] = '\0';
             return;
@@ -1373,8 +1440,7 @@ void fill_pathname_home_dir(char *s, size_t len)
 bool is_path_accessible_using_standard_io(const char *path)
 {
 #ifdef __WINRT__
-   DWORD trygetattrbs = GetFileAttributesA(path);
-   return trygetattrbs != INVALID_FILE_ATTRIBUTES;
+   return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
 #else
    return true;
 #endif
diff --git a/platform/libretro/libretro-common/file/file_path_io.c b/platform/libretro/libretro-common/file/file_path_io.c
new file mode 100644 (file)
index 0000000..d8f8187
--- /dev/null
@@ -0,0 +1,149 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (file_path_io.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/stat.h>
+
+#include <boolean.h>
+#include <file/file_path.h>
+#include <compat/strl.h>
+#include <compat/posix_string.h>
+#include <retro_miscellaneous.h>
+#include <string/stdstring.h>
+#define VFS_FRONTEND
+#include <vfs/vfs_implementation.h>
+
+#ifdef _WIN32
+#include <direct.h>
+#else
+#include <unistd.h> /* stat() is defined here */
+#endif
+
+/* TODO/FIXME - globals */
+static retro_vfs_stat_t path_stat_cb   = retro_vfs_stat_impl;
+static retro_vfs_mkdir_t path_mkdir_cb = retro_vfs_mkdir_impl;
+
+void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
+{
+   const struct retro_vfs_interface* 
+      vfs_iface           = vfs_info->iface;
+
+   path_stat_cb           = retro_vfs_stat_impl;
+   path_mkdir_cb          = retro_vfs_mkdir_impl;
+
+   if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface)
+      return;
+
+   path_stat_cb           = vfs_iface->stat;
+   path_mkdir_cb          = vfs_iface->mkdir;
+}
+
+int path_stat(const char *path)
+{
+   return path_stat_cb(path, NULL);
+}
+
+/**
+ * path_is_directory:
+ * @path               : path
+ *
+ * Checks if path is a directory.
+ *
+ * @return true if path is a directory, otherwise false.
+ */
+bool path_is_directory(const char *path)
+{
+   return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
+}
+
+bool path_is_character_special(const char *path)
+{
+   return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
+}
+
+bool path_is_valid(const char *path)
+{
+   return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
+}
+
+int32_t path_get_size(const char *path)
+{
+   int32_t filesize = 0;
+   if (path_stat_cb(path, &filesize) != 0)
+      return filesize;
+
+   return -1;
+}
+
+/**
+ * path_mkdir:
+ * @dir                : directory
+ *
+ * Create directory on filesystem.
+ * 
+ * Recursive function.
+ *
+ * @return true if directory could be created, otherwise false.
+ **/
+bool path_mkdir(const char *dir)
+{
+   bool norecurse     = false;
+   char     *basedir  = NULL;
+
+   if (!(dir && *dir))
+      return false;
+
+   /* Use heap. Real chance of stack 
+    * overflow if we recurse too hard. */
+   if (!(basedir = strdup(dir)))
+      return false;
+
+   path_parent_dir(basedir, strlen(basedir));
+
+   if (!*basedir || !strcmp(basedir, dir))
+   {
+      free(basedir);
+      return false;
+   }
+
+   if (     path_is_directory(basedir)
+         || path_mkdir(basedir))
+      norecurse = true;
+
+   free(basedir);
+
+   if (norecurse)
+   {
+      int ret = path_mkdir_cb(dir);
+
+      /* Don't treat this as an error. */
+      if (ret == -2 && path_is_directory(dir))
+         return true;
+      else if (ret == 0)
+         return true;
+   }
+   return false;
+}
diff --git a/platform/libretro/libretro-common/formats/png/rpng.c b/platform/libretro/libretro-common/formats/png/rpng.c
new file mode 100644 (file)
index 0000000..9cd19f6
--- /dev/null
@@ -0,0 +1,1251 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (rpng.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef GEKKO
+#include <malloc.h>
+#endif
+
+#include <boolean.h>
+#include <formats/image.h>
+#include <formats/rpng.h>
+#include <streams/trans_stream.h>
+#include <string/stdstring.h>
+
+#include "rpng_internal.h"
+
+enum png_ihdr_color_type
+{
+   PNG_IHDR_COLOR_GRAY       = 0,
+   PNG_IHDR_COLOR_RGB        = 2,
+   PNG_IHDR_COLOR_PLT        = 3,
+   PNG_IHDR_COLOR_GRAY_ALPHA = 4,
+   PNG_IHDR_COLOR_RGBA       = 6
+};
+
+enum png_line_filter
+{
+   PNG_FILTER_NONE = 0,
+   PNG_FILTER_SUB,
+   PNG_FILTER_UP,
+   PNG_FILTER_AVERAGE,
+   PNG_FILTER_PAETH
+};
+
+enum png_chunk_type
+{
+   PNG_CHUNK_NOOP = 0,
+   PNG_CHUNK_ERROR,
+   PNG_CHUNK_IHDR,
+   PNG_CHUNK_IDAT,
+   PNG_CHUNK_PLTE,
+   PNG_CHUNK_tRNS,
+   PNG_CHUNK_IEND
+};
+
+struct adam7_pass
+{
+   unsigned x;
+   unsigned y;
+   unsigned stride_x;
+   unsigned stride_y;
+};
+
+struct idat_buffer
+{
+   uint8_t *data;
+   size_t size;
+};
+
+enum rpng_process_flags
+{
+   RPNG_PROCESS_FLAG_INFLATE_INITIALIZED    = (1 << 0),
+   RPNG_PROCESS_FLAG_ADAM7_PASS_INITIALIZED = (1 << 1),
+   RPNG_PROCESS_FLAG_PASS_INITIALIZED       = (1 << 2)
+};
+
+struct rpng_process
+{
+   uint32_t *data;
+   uint32_t *palette;
+   void *stream;
+   const struct trans_stream_backend *stream_backend;
+   uint8_t *prev_scanline;
+   uint8_t *decoded_scanline;
+   uint8_t *inflate_buf;
+   size_t restore_buf_size;
+   size_t adam7_restore_buf_size;
+   size_t data_restore_buf_size;
+   size_t inflate_buf_size;
+   size_t avail_in;
+   size_t avail_out;
+   size_t total_out;
+   size_t pass_size;
+   struct png_ihdr ihdr; /* uint32_t alignment */
+   unsigned bpp;
+   unsigned pitch;
+   unsigned h;
+   unsigned pass_width;
+   unsigned pass_height;
+   unsigned pass_pos;
+   uint8_t flags;
+};
+
+enum rpng_flags
+{
+   RPNG_FLAG_HAS_IHDR = (1 << 0),
+   RPNG_FLAG_HAS_IDAT = (1 << 1),
+   RPNG_FLAG_HAS_IEND = (1 << 2),
+   RPNG_FLAG_HAS_PLTE = (1 << 3),
+   RPNG_FLAG_HAS_TRNS = (1 << 4)
+};
+
+struct rpng
+{
+   struct rpng_process *process;
+   uint8_t *buff_data;
+   uint8_t *buff_end;
+   struct idat_buffer idat_buf; /* ptr alignment */
+   struct png_ihdr ihdr; /* uint32 alignment */
+   uint32_t palette[256];
+   uint8_t flags;
+};
+
+static const struct adam7_pass rpng_passes[] = {
+   { 0, 0, 8, 8 },
+   { 4, 0, 8, 8 },
+   { 0, 4, 4, 8 },
+   { 2, 0, 4, 4 },
+   { 0, 2, 2, 4 },
+   { 1, 0, 2, 2 },
+   { 0, 1, 1, 2 },
+};
+
+static INLINE uint32_t rpng_dword_be(const uint8_t *buf)
+{
+   return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] << 0);
+}
+
+#if defined(DEBUG) || defined(RPNG_TEST)
+static bool rpng_process_ihdr(struct png_ihdr *ihdr)
+{
+   uint8_t ihdr_depth = ihdr->depth;
+
+   switch (ihdr->color_type)
+   {
+      case PNG_IHDR_COLOR_RGB:
+      case PNG_IHDR_COLOR_GRAY_ALPHA:
+      case PNG_IHDR_COLOR_RGBA:
+         if (ihdr_depth != 8 && ihdr_depth != 16)
+         {
+            fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
+            return false;
+         }
+         break;
+      case PNG_IHDR_COLOR_GRAY:
+         /* Valid bitdepths are: 1, 2, 4, 8, 16 */
+         if (ihdr_depth > 16 || (0x977F7FFF << ihdr_depth) & 0x80000000)
+         {
+            fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
+            return false;
+         }
+         break;
+      case PNG_IHDR_COLOR_PLT:
+         /* Valid bitdepths are: 1, 2, 4, 8 */
+         if (ihdr_depth > 8 || (0x977F7FFF << ihdr_depth)  & 0x80000000)
+         {
+            fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
+            return false;
+         }
+         break;
+      default:
+         fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
+         return false;
+   }
+
+#ifdef RPNG_TEST
+   fprintf(stderr, "IHDR: (%u x %u), bpc = %u, palette = %s, color = %s, alpha = %s, adam7 = %s.\n",
+         ihdr->width, ihdr->height,
+         ihdr_depth, (ihdr->color_type == PNG_IHDR_COLOR_PLT) ? "yes" : "no",
+         (ihdr->color_type & PNG_IHDR_COLOR_RGB)              ? "yes" : "no",
+         (ihdr->color_type & PNG_IHDR_COLOR_GRAY_ALPHA)       ? "yes" : "no",
+         ihdr->interlace == 1 ? "yes" : "no");
+#endif
+
+   return true;
+}
+#else
+static bool rpng_process_ihdr(struct png_ihdr *ihdr)
+{
+   uint8_t ihdr_depth = ihdr->depth;
+
+   switch (ihdr->color_type)
+   {
+      case PNG_IHDR_COLOR_RGB:
+      case PNG_IHDR_COLOR_GRAY_ALPHA:
+      case PNG_IHDR_COLOR_RGBA:
+         if (ihdr_depth != 8 && ihdr_depth != 16)
+            return false;
+         break;
+      case PNG_IHDR_COLOR_GRAY:
+         /* Valid bitdepths are: 1, 2, 4, 8, 16 */
+         if (ihdr_depth > 16 || (0x977F7FFF << ihdr_depth) & 0x80000000)
+            return false;
+         break;
+      case PNG_IHDR_COLOR_PLT:
+         /* Valid bitdepths are: 1, 2, 4, 8 */
+         if (ihdr_depth > 8 || (0x977F7FFF << ihdr_depth)  & 0x80000000)
+            return false;
+         break;
+      default:
+         return false;
+   }
+
+   return true;
+}
+#endif
+
+static void rpng_reverse_filter_copy_line_rgb(uint32_t *data,
+      const uint8_t *decoded, unsigned width, unsigned bpp)
+{
+   int i;
+
+   bpp /= 8;
+
+   for (i = 0; i < (int)width; i++)
+   {
+      uint32_t r, g, b;
+
+      r        = *decoded;
+      decoded += bpp;
+      g        = *decoded;
+      decoded += bpp;
+      b        = *decoded;
+      decoded += bpp;
+      data[i]  = (0xffu << 24) | (r << 16) | (g << 8) | (b << 0);
+   }
+}
+
+static void rpng_reverse_filter_copy_line_rgba(uint32_t *data,
+      const uint8_t *decoded, unsigned width, unsigned bpp)
+{
+   int i;
+
+   bpp /= 8;
+
+   for (i = 0; i < (int)width; i++)
+   {
+      uint32_t r, g, b, a;
+      r        = *decoded;
+      decoded += bpp;
+      g        = *decoded;
+      decoded += bpp;
+      b        = *decoded;
+      decoded += bpp;
+      a        = *decoded;
+      decoded += bpp;
+      data[i]  = (a << 24) | (r << 16) | (g << 8) | (b << 0);
+   }
+}
+
+static void rpng_reverse_filter_copy_line_bw(uint32_t *data,
+      const uint8_t *decoded, unsigned width, unsigned depth)
+{
+   int i;
+   unsigned bit;
+   static const unsigned mul_table[] = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 };
+   unsigned mul, mask;
+
+   if (depth == 16)
+   {
+      for (i = 0; i < (int)width; i++)
+      {
+         uint32_t val = decoded[i << 1];
+         data[i]      = (val * 0x010101) | (0xffu << 24);
+      }
+      return;
+   }
+
+   mul  = mul_table[depth];
+   mask = (1 << depth) - 1;
+   bit  = 0;
+
+   for (i = 0; i < (int)width; i++, bit += depth)
+   {
+      unsigned byte = bit >> 3;
+      unsigned val  = decoded[byte] >> (8 - depth - (bit & 7));
+
+      val          &= mask;
+      val          *= mul;
+      data[i]       = (val * 0x010101) | (0xffu << 24);
+   }
+}
+
+static void rpng_reverse_filter_copy_line_gray_alpha(uint32_t *data,
+      const uint8_t *decoded, unsigned width,
+      unsigned bpp)
+{
+   int i;
+
+   bpp /= 8;
+
+   for (i = 0; i < (int)width; i++)
+   {
+      uint32_t gray, alpha;
+
+      gray     = *decoded;
+      decoded += bpp;
+      alpha    = *decoded;
+      decoded += bpp;
+
+      data[i]  = (gray * 0x010101) | (alpha << 24);
+   }
+}
+
+static void rpng_reverse_filter_copy_line_plt(uint32_t *data,
+      const uint8_t *decoded, unsigned width,
+      unsigned depth, const uint32_t *palette)
+{
+   switch (depth)
+   {
+      case 1:
+         {
+            int i;
+            unsigned w = width / 8;
+            for (i = 0; i < (int)w; i++, decoded++)
+            {
+               *data++ = palette[(*decoded >> 7) & 1];
+               *data++ = palette[(*decoded >> 6) & 1];
+               *data++ = palette[(*decoded >> 5) & 1];
+               *data++ = palette[(*decoded >> 4) & 1];
+               *data++ = palette[(*decoded >> 3) & 1];
+               *data++ = palette[(*decoded >> 2) & 1];
+               *data++ = palette[(*decoded >> 1) & 1];
+               *data++ = palette[*decoded & 1];
+            }
+
+            switch (width & 7)
+            {
+               case 7:
+                  data[6] = palette[(*decoded >> 1) & 1];
+               case 6:
+                  data[5] = palette[(*decoded >> 2) & 1];
+               case 5:
+                  data[4] = palette[(*decoded >> 3) & 1];
+               case 4:
+                  data[3] = palette[(*decoded >> 4) & 1];
+               case 3:
+                  data[2] = palette[(*decoded >> 5) & 1];
+               case 2:
+                  data[1] = palette[(*decoded >> 6) & 1];
+               case 1:
+                  data[0] = palette[(*decoded >> 7) & 1];
+                  break;
+            }
+         }
+         break;
+
+      case 2:
+         {
+            int i;
+            unsigned w = width / 4;
+            for (i = 0; i < (int)w; i++, decoded++)
+            {
+               *data++ = palette[(*decoded >> 6) & 3];
+               *data++ = palette[(*decoded >> 4) & 3];
+               *data++ = palette[(*decoded >> 2) & 3];
+               *data++ = palette[*decoded & 3];
+            }
+
+            switch (width & 3)
+            {
+               case 3:
+                  data[2] = palette[(*decoded >> 2) & 3];
+               case 2:
+                  data[1] = palette[(*decoded >> 4) & 3];
+               case 1:
+                  data[0] = palette[(*decoded >> 6) & 3];
+                  break;
+            }
+         }
+         break;
+
+      case 4:
+         {
+            int i;
+            unsigned w = width / 2;
+            for (i = 0; i < (int)w; i++, decoded++)
+            {
+               *data++ = palette[*decoded >> 4];
+               *data++ = palette[*decoded & 0x0f];
+            }
+
+            if (width & 1)
+               *data = palette[*decoded >> 4];
+         }
+         break;
+
+      case 8:
+         {
+            int i;
+            for (i = 0; i < (int)width; i++, decoded++, data++)
+               *data = palette[*decoded];
+         }
+         break;
+   }
+}
+
+static void rpng_pass_geom(const struct png_ihdr *ihdr,
+      unsigned width, unsigned height,
+      unsigned *bpp_out, unsigned *pitch_out, size_t *pass_size)
+{
+   unsigned bpp   = 0;
+   unsigned pitch = 0;
+
+   switch (ihdr->color_type)
+   {
+      case PNG_IHDR_COLOR_GRAY:
+         bpp   = (ihdr->depth + 7) / 8;
+         pitch = (ihdr->width * ihdr->depth + 7) / 8;
+         break;
+      case PNG_IHDR_COLOR_RGB:
+         bpp   = (ihdr->depth * 3 + 7) / 8;
+         pitch = (ihdr->width * ihdr->depth * 3 + 7) / 8;
+         break;
+      case PNG_IHDR_COLOR_PLT:
+         bpp   = (ihdr->depth + 7) / 8;
+         pitch = (ihdr->width * ihdr->depth + 7) / 8;
+         break;
+      case PNG_IHDR_COLOR_GRAY_ALPHA:
+         bpp   = (ihdr->depth * 2 + 7) / 8;
+         pitch = (ihdr->width * ihdr->depth * 2 + 7) / 8;
+         break;
+      case PNG_IHDR_COLOR_RGBA:
+         bpp   = (ihdr->depth * 4 + 7) / 8;
+         pitch = (ihdr->width * ihdr->depth * 4 + 7) / 8;
+         break;
+      default:
+         break;
+   }
+
+   if (pass_size)
+      *pass_size = (pitch + 1) * ihdr->height;
+   if (bpp_out)
+      *bpp_out   = bpp;
+   if (pitch_out)
+      *pitch_out = pitch;
+}
+
+static void rpng_reverse_filter_adam7_deinterlace_pass(uint32_t *data,
+      const struct png_ihdr *ihdr,
+      const uint32_t *input, unsigned pass_width, unsigned pass_height,
+      const struct adam7_pass *pass)
+{
+   unsigned x, y;
+
+   data += pass->y * ihdr->width + pass->x;
+
+   for (y = 0; y < pass_height;
+         y++, data += ihdr->width * pass->stride_y, input += pass_width)
+   {
+      uint32_t *out = data;
+
+      for (x = 0; x < pass_width; x++, out += pass->stride_x)
+         *out = input[x];
+   }
+}
+
+static void rpng_reverse_filter_deinit(struct rpng_process *pngp)
+{
+   if (!pngp)
+      return;
+   if (pngp->decoded_scanline)
+      free(pngp->decoded_scanline);
+   pngp->decoded_scanline = NULL;
+   if (pngp->prev_scanline)
+      free(pngp->prev_scanline);
+   pngp->prev_scanline    = NULL;
+
+   pngp->flags           &= ~RPNG_PROCESS_FLAG_PASS_INITIALIZED;
+   pngp->h                = 0;
+}
+
+static int rpng_reverse_filter_init(const struct png_ihdr *ihdr,
+      struct rpng_process *pngp)
+{
+   size_t pass_size;
+
+   if (   !(pngp->flags & RPNG_PROCESS_FLAG_ADAM7_PASS_INITIALIZED) 
+         && ihdr->interlace)
+   {
+      if (     ihdr->width  <= rpng_passes[pngp->pass_pos].x
+            || ihdr->height <= rpng_passes[pngp->pass_pos].y) /* Empty pass */
+         return 1;
+
+      pngp->pass_width  = (ihdr->width -
+            rpng_passes[pngp->pass_pos].x + rpng_passes[pngp->pass_pos].stride_x
+- 1) / rpng_passes[pngp->pass_pos].stride_x;
+      pngp->pass_height = (ihdr->height - rpng_passes[pngp->pass_pos].y +
+            rpng_passes[pngp->pass_pos].stride_y - 1) / rpng_passes[pngp->pass_pos].stride_y;
+
+      if (!(pngp->data = (uint32_t*)malloc(
+            pngp->pass_width * pngp->pass_height * sizeof(uint32_t))))
+         return -1;
+
+      pngp->ihdr        = *ihdr;
+      pngp->ihdr.width  = pngp->pass_width;
+      pngp->ihdr.height = pngp->pass_height;
+
+      rpng_pass_geom(&pngp->ihdr, pngp->pass_width,
+            pngp->pass_height, NULL, NULL, &pngp->pass_size);
+
+      if (pngp->pass_size > pngp->total_out)
+      {
+         free(pngp->data);
+         pngp->data = NULL;
+         return -1;
+      }
+
+      pngp->flags |= RPNG_PROCESS_FLAG_ADAM7_PASS_INITIALIZED;
+
+      return 0;
+   }
+
+   if (pngp->flags & RPNG_PROCESS_FLAG_PASS_INITIALIZED)
+      return 0;
+
+   rpng_pass_geom(ihdr, ihdr->width, ihdr->height, &pngp->bpp, &pngp->pitch, &pass_size);
+
+   if (pngp->total_out < pass_size)
+      return -1;
+
+   pngp->restore_buf_size      = 0;
+   pngp->data_restore_buf_size = 0;
+   pngp->prev_scanline         = (uint8_t*)calloc(1, pngp->pitch);
+   pngp->decoded_scanline      = (uint8_t*)calloc(1, pngp->pitch);
+
+   if (!pngp->prev_scanline || !pngp->decoded_scanline)
+      goto error;
+
+   pngp->h                    = 0;
+   pngp->flags               |= RPNG_PROCESS_FLAG_PASS_INITIALIZED;
+
+   return 0;
+
+error:
+   rpng_reverse_filter_deinit(pngp);
+   return -1;
+}
+
+static int rpng_reverse_filter_copy_line(uint32_t *data,
+      const struct png_ihdr *ihdr,
+      struct rpng_process *pngp, unsigned filter)
+{
+   unsigned i;
+
+   switch (filter)
+   {
+      case PNG_FILTER_NONE:
+         memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch);
+         break;
+      case PNG_FILTER_SUB:
+         memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch);
+         for (i = pngp->bpp; i < pngp->pitch; i++)
+            pngp->decoded_scanline[i] += pngp->decoded_scanline[i - pngp->bpp];
+         break;
+      case PNG_FILTER_UP:
+         memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch);
+         for (i = 0; i < pngp->pitch; i++)
+            pngp->decoded_scanline[i] += pngp->prev_scanline[i];
+         break;
+      case PNG_FILTER_AVERAGE:
+         memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch);
+         for (i = 0; i < pngp->bpp; i++)
+         {
+            uint8_t avg = pngp->prev_scanline[i] >> 1;
+            pngp->decoded_scanline[i] += avg;
+         }
+         for (i = pngp->bpp; i < pngp->pitch; i++)
+         {
+            uint8_t avg = (pngp->decoded_scanline[i - pngp->bpp] + pngp->prev_scanline[i]) >> 1;
+            pngp->decoded_scanline[i] += avg;
+         }
+         break;
+      case PNG_FILTER_PAETH:
+         memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch);
+         for (i = 0; i < pngp->bpp; i++)
+            pngp->decoded_scanline[i] += pngp->prev_scanline[i];
+         for (i = pngp->bpp; i < pngp->pitch; i++)
+            pngp->decoded_scanline[i] += paeth(pngp->decoded_scanline[i - pngp->bpp],
+                  pngp->prev_scanline[i], pngp->prev_scanline[i - pngp->bpp]);
+         break;
+      default:
+         return IMAGE_PROCESS_ERROR_END;
+   }
+
+   switch (ihdr->color_type)
+   {
+      case PNG_IHDR_COLOR_GRAY:
+         rpng_reverse_filter_copy_line_bw(data, pngp->decoded_scanline, ihdr->width, ihdr->depth);
+         break;
+      case PNG_IHDR_COLOR_RGB:
+         rpng_reverse_filter_copy_line_rgb(data, pngp->decoded_scanline, ihdr->width, ihdr->depth);
+         break;
+      case PNG_IHDR_COLOR_PLT:
+         rpng_reverse_filter_copy_line_plt(
+               data, pngp->decoded_scanline, ihdr->width,
+               ihdr->depth, pngp->palette);
+         break;
+      case PNG_IHDR_COLOR_GRAY_ALPHA:
+         rpng_reverse_filter_copy_line_gray_alpha(data, pngp->decoded_scanline, ihdr->width,
+               ihdr->depth);
+         break;
+      case PNG_IHDR_COLOR_RGBA:
+         rpng_reverse_filter_copy_line_rgba(data, pngp->decoded_scanline, ihdr->width, ihdr->depth);
+         break;
+   }
+
+   memcpy(pngp->prev_scanline, pngp->decoded_scanline, pngp->pitch);
+
+   return IMAGE_PROCESS_NEXT;
+}
+
+static int rpng_reverse_filter_regular_iterate(
+      uint32_t **data, const struct png_ihdr *ihdr,
+      struct rpng_process *pngp)
+{
+   int ret = IMAGE_PROCESS_END;
+   if (pngp->h < ihdr->height)
+   {
+      unsigned filter         = *pngp->inflate_buf++;
+      pngp->restore_buf_size += 1;
+      ret                     = rpng_reverse_filter_copy_line(*data,
+            ihdr, pngp, filter);
+      if (ret == IMAGE_PROCESS_END || ret == IMAGE_PROCESS_ERROR_END)
+         goto end;
+   }
+   else
+      goto end;
+
+   pngp->h++;
+   pngp->inflate_buf           += pngp->pitch;
+   pngp->restore_buf_size      += pngp->pitch;
+
+   *data                       += ihdr->width;
+   pngp->data_restore_buf_size += ihdr->width;
+
+   return IMAGE_PROCESS_NEXT;
+
+end:
+   rpng_reverse_filter_deinit(pngp);
+
+   pngp->inflate_buf -= pngp->restore_buf_size;
+   *data             -= pngp->data_restore_buf_size;
+   pngp->data_restore_buf_size = 0;
+   return ret;
+}
+
+static int rpng_reverse_filter_adam7_iterate(uint32_t **data_,
+      const struct png_ihdr *ihdr,
+      struct rpng_process *pngp)
+{
+   int        ret = 0;
+   bool   to_next = pngp->pass_pos < ARRAY_SIZE(rpng_passes);
+   uint32_t *data = *data_;
+
+   if (!to_next)
+      return IMAGE_PROCESS_END;
+
+   if ((ret = rpng_reverse_filter_init(ihdr, pngp)) == 1)
+      return IMAGE_PROCESS_NEXT;
+   else if (ret == -1)
+      return IMAGE_PROCESS_ERROR_END;
+
+   if (rpng_reverse_filter_init(&pngp->ihdr, pngp) == -1)
+      return IMAGE_PROCESS_ERROR;
+
+   do
+   {
+      ret = rpng_reverse_filter_regular_iterate(&pngp->data,
+            &pngp->ihdr, pngp);
+   } while (ret == IMAGE_PROCESS_NEXT);
+
+   if (ret == IMAGE_PROCESS_ERROR || ret == IMAGE_PROCESS_ERROR_END)
+      return IMAGE_PROCESS_ERROR;
+
+   pngp->inflate_buf            += pngp->pass_size;
+   pngp->adam7_restore_buf_size += pngp->pass_size;
+
+   pngp->total_out              -= pngp->pass_size;
+
+   rpng_reverse_filter_adam7_deinterlace_pass(data,
+         ihdr, pngp->data, pngp->pass_width, pngp->pass_height,
+         &rpng_passes[pngp->pass_pos]);
+
+   free(pngp->data);
+
+   pngp->data                   = NULL;
+   pngp->pass_width             = 0;
+   pngp->pass_height            = 0;
+   pngp->pass_size              = 0;
+   pngp->flags                 &= ~RPNG_PROCESS_FLAG_ADAM7_PASS_INITIALIZED;
+
+   return IMAGE_PROCESS_NEXT;
+}
+
+static int rpng_reverse_filter_adam7(uint32_t **data_,
+      const struct png_ihdr *ihdr,
+      struct rpng_process *pngp)
+{
+   int ret = rpng_reverse_filter_adam7_iterate(data_,
+         ihdr, pngp);
+
+   switch (ret)
+   {
+      case IMAGE_PROCESS_ERROR_END:
+      case IMAGE_PROCESS_END:
+         break;
+      case IMAGE_PROCESS_NEXT:
+         pngp->pass_pos++;
+         return 0;
+      case IMAGE_PROCESS_ERROR:
+         if (pngp->data)
+         {
+            free(pngp->data);
+            pngp->data = NULL;
+         }
+         pngp->inflate_buf -= pngp->adam7_restore_buf_size;
+         pngp->adam7_restore_buf_size = 0;
+         return -1;
+   }
+
+   pngp->inflate_buf            -= pngp->adam7_restore_buf_size;
+   pngp->adam7_restore_buf_size  = 0;
+   return ret;
+}
+
+static int rpng_load_image_argb_process_inflate_init(
+      rpng_t *rpng, uint32_t **data)
+{
+   bool zstatus;
+   enum trans_stream_error terror;
+   uint32_t rd, wn;
+   struct rpng_process *process = (struct rpng_process*)rpng->process;
+   bool to_continue        = (process->avail_in > 0
+         && process->avail_out > 0);
+
+   if (!to_continue)
+      goto end;
+
+   zstatus = process->stream_backend->trans(process->stream, false, &rd, &wn, &terror);
+
+   if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL)
+      goto error;
+
+   process->avail_in -= rd;
+   process->avail_out -= wn;
+   process->total_out += wn;
+
+   if (terror)
+      return 0;
+
+end:
+   process->stream_backend->stream_free(process->stream);
+   process->stream = NULL;
+
+#ifdef GEKKO
+   /* we often use these in textures, make sure they're 32-byte aligned */
+   *data = (uint32_t*)memalign(32, rpng->ihdr.width *
+         rpng->ihdr.height * sizeof(uint32_t));
+#else
+   *data = (uint32_t*)malloc(rpng->ihdr.width *
+         rpng->ihdr.height * sizeof(uint32_t));
+#endif
+   if (!*data)
+      goto false_end;
+
+   process->adam7_restore_buf_size = 0;
+   process->restore_buf_size       = 0;
+   process->palette                = rpng->palette;
+
+   if (rpng->ihdr.interlace != 1)
+      if (rpng_reverse_filter_init(&rpng->ihdr, process) == -1)
+         goto false_end;
+
+   process->flags              |=  RPNG_PROCESS_FLAG_INFLATE_INITIALIZED;
+   return 1;
+
+error:
+false_end:
+   process->flags              &= ~RPNG_PROCESS_FLAG_INFLATE_INITIALIZED;
+   return -1;
+}
+
+static bool rpng_realloc_idat(struct idat_buffer *buf, uint32_t chunk_size)
+{
+   uint8_t *new_buffer = (uint8_t*)realloc(buf->data, buf->size + chunk_size);
+
+   if (!new_buffer)
+      return false;
+
+   buf->data  = new_buffer;
+   return true;
+}
+
+static struct rpng_process *rpng_process_init(rpng_t *rpng)
+{
+   uint8_t *inflate_buf            = NULL;
+   struct rpng_process *process    = (struct rpng_process*)malloc(sizeof(*process));
+
+   if (!process)
+      return NULL;
+
+   process->flags                  = 0;
+   process->prev_scanline          = NULL;
+   process->decoded_scanline       = NULL;
+   process->inflate_buf            = NULL;
+
+   process->ihdr.width             = 0;
+   process->ihdr.height            = 0;
+   process->ihdr.depth             = 0;
+   process->ihdr.color_type        = 0;
+   process->ihdr.compression       = 0;
+   process->ihdr.filter            = 0;
+   process->ihdr.interlace         = 0;
+
+   process->restore_buf_size       = 0;
+   process->adam7_restore_buf_size = 0;
+   process->data_restore_buf_size  = 0;
+   process->inflate_buf_size       = 0;
+   process->avail_in               = 0;
+   process->avail_out              = 0;
+   process->total_out              = 0;
+   process->pass_size              = 0;
+   process->bpp                    = 0;
+   process->pitch                  = 0;
+   process->h                      = 0;
+   process->pass_width             = 0;
+   process->pass_height            = 0;
+   process->pass_pos               = 0;
+   process->data                   = 0;
+   process->palette                = 0;
+   process->stream                 = NULL;
+   process->stream_backend         = trans_stream_get_zlib_inflate_backend();
+
+   rpng_pass_geom(&rpng->ihdr, rpng->ihdr.width,
+         rpng->ihdr.height, NULL, NULL, &process->inflate_buf_size);
+   if (rpng->ihdr.interlace == 1) /* To be sure. */
+      process->inflate_buf_size *= 2;
+
+   process->stream = process->stream_backend->stream_new();
+
+   if (!process->stream)
+   {
+      free(process);
+      return NULL;
+   }
+
+   inflate_buf = (uint8_t*)malloc(process->inflate_buf_size);
+   if (!inflate_buf)
+      goto error;
+
+   process->inflate_buf = inflate_buf;
+   process->avail_in    = rpng->idat_buf.size;
+   process->avail_out   = process->inflate_buf_size;
+
+   process->stream_backend->set_in(
+         process->stream,
+         rpng->idat_buf.data,
+         (uint32_t)rpng->idat_buf.size);
+   process->stream_backend->set_out(
+         process->stream,
+         process->inflate_buf,
+         (uint32_t)process->inflate_buf_size);
+
+   return process;
+
+error:
+   if (process)
+   {
+      if (process->stream)
+         process->stream_backend->stream_free(process->stream);
+      free(process);
+   }
+   return NULL;
+}
+
+/**
+ * rpng_read_chunk_header:
+ *
+ * Leaf function.
+ *
+ * @return The PNG type of the memory chunk (i.e. IHDR, IDAT, IEND,
+   PLTE, and/or tRNS)
+ **/
+static enum png_chunk_type rpng_read_chunk_header(
+      uint8_t *buf, uint32_t chunk_size)
+{
+   int i;
+   char type[4];
+
+   for (i = 0; i < 4; i++)
+   {
+      uint8_t byte = buf[i + 4];
+
+      /* All four bytes of the chunk type must be
+       * ASCII letters (codes 65-90 and 97-122) */
+      if ((byte < 65) || ((byte > 90) && (byte < 97)) || (byte > 122))
+         return PNG_CHUNK_ERROR;
+      type[i]      = byte;
+   }
+
+   if (     
+            type[0] == 'I'
+         && type[1] == 'H'
+         && type[2] == 'D'
+         && type[3] == 'R'
+      )
+      return PNG_CHUNK_IHDR;
+   else if
+      (
+          type[0] == 'I'
+       && type[1] == 'D'
+       && type[2] == 'A'
+       && type[3] == 'T'
+      )
+         return PNG_CHUNK_IDAT;
+   else if
+      (
+          type[0] == 'I'
+       && type[1] == 'E'
+       && type[2] == 'N'
+       && type[3] == 'D'
+      )
+         return PNG_CHUNK_IEND;
+   else if
+      (
+          type[0] == 'P'
+       && type[1] == 'L'
+       && type[2] == 'T'
+       && type[3] == 'E'
+      )
+         return PNG_CHUNK_PLTE;
+   else if
+      (
+          type[0] == 't'
+       && type[1] == 'R'
+       && type[2] == 'N'
+       && type[3] == 'S'
+      )
+         return PNG_CHUNK_tRNS;
+
+   return PNG_CHUNK_NOOP;
+}
+
+bool rpng_iterate_image(rpng_t *rpng)
+{
+   unsigned i;
+   uint8_t *buf             = (uint8_t*)rpng->buff_data;
+   uint32_t chunk_size      = 0;
+
+   /* Check whether data buffer pointer is valid */
+   if (buf > rpng->buff_end)
+      return false;
+
+   /* Check whether reading the header will overflow
+    * the data buffer */
+   if (rpng->buff_end - buf < 8)
+      return false;
+
+   chunk_size = rpng_dword_be(buf);
+
+   /* Check whether chunk will overflow the data buffer */
+   if (buf + 8 + chunk_size > rpng->buff_end)
+      return false;
+
+   switch (rpng_read_chunk_header(buf, chunk_size))
+   {
+      case PNG_CHUNK_NOOP:
+      default:
+         break;
+
+      case PNG_CHUNK_ERROR:
+         return false;
+
+      case PNG_CHUNK_IHDR:
+         if (     (rpng->flags & RPNG_FLAG_HAS_IHDR) 
+               || (rpng->flags & RPNG_FLAG_HAS_IDAT)
+               || (rpng->flags & RPNG_FLAG_HAS_IEND))
+            return false;
+
+         if (chunk_size != 13)
+            return false;
+
+         buf                    += 4 + 4;
+
+         rpng->ihdr.width        = rpng_dword_be(buf + 0);
+         rpng->ihdr.height       = rpng_dword_be(buf + 4);
+         rpng->ihdr.depth        = buf[8];
+         rpng->ihdr.color_type   = buf[9];
+         rpng->ihdr.compression  = buf[10];
+         rpng->ihdr.filter       = buf[11];
+         rpng->ihdr.interlace    = buf[12];
+
+         if (     rpng->ihdr.width  == 0 
+               || rpng->ihdr.height == 0 
+               /* ensure multiplications don't overflow and wrap around, that'd give buffer overflow crashes */
+               || (uint64_t)rpng->ihdr.width*rpng->ihdr.height*sizeof(uint32_t) >= 0x80000000)
+            return false;
+
+         if (!rpng_process_ihdr(&rpng->ihdr))
+            return false;
+
+         if (rpng->ihdr.compression != 0)
+         {
+#if defined(DEBUG) || defined(RPNG_TEST)
+            fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
+#endif
+            return false;
+         }
+
+         rpng->flags   |= RPNG_FLAG_HAS_IHDR;
+         break;
+
+      case PNG_CHUNK_PLTE:
+         {
+            int i;
+            unsigned entries = chunk_size / 3;
+
+            if (entries > 256)
+               return false;
+            if (chunk_size % 3)
+               return false;
+
+            if (     !(rpng->flags & RPNG_FLAG_HAS_IHDR)
+                  ||  (rpng->flags & RPNG_FLAG_HAS_PLTE)
+                  ||  (rpng->flags & RPNG_FLAG_HAS_IEND)
+                  ||  (rpng->flags & RPNG_FLAG_HAS_IDAT)
+                  ||  (rpng->flags & RPNG_FLAG_HAS_TRNS))
+               return false;
+
+            buf += 8;
+
+            for (i = 0; i < (int)entries; i++)
+            {
+               uint32_t r       = buf[3 * i + 0];
+               uint32_t g       = buf[3 * i + 1];
+               uint32_t b       = buf[3 * i + 2];
+               rpng->palette[i] = (r << 16) | (g << 8) | (b << 0) | (0xffu << 24);
+            }
+
+            rpng->flags        |= RPNG_FLAG_HAS_PLTE;
+         }
+         break;
+
+      case PNG_CHUNK_tRNS:
+         if (rpng->flags & RPNG_FLAG_HAS_IDAT)
+            return false;
+
+         if (rpng->ihdr.color_type == PNG_IHDR_COLOR_PLT)
+         {
+            int i;
+            uint32_t *palette;
+            /* we should compare with the number of palette entries */
+            if (chunk_size > 256)
+               return false;
+
+            buf    += 8;
+            palette = rpng->palette;
+
+            for (i = 0; i < (int)chunk_size; i++, buf++, palette++)
+               *palette = (*palette & 0x00ffffff) | (unsigned)*buf << 24;
+         }
+         /* TODO: support colorkey in grayscale and truecolor images */
+
+         rpng->flags         |= RPNG_FLAG_HAS_TRNS;
+         break;
+
+      case PNG_CHUNK_IDAT:
+         if (     !(rpng->flags & RPNG_FLAG_HAS_IHDR) 
+               ||  (rpng->flags & RPNG_FLAG_HAS_IEND)
+               ||  (rpng->ihdr.color_type == PNG_IHDR_COLOR_PLT 
+                  && 
+                  !(rpng->flags & RPNG_FLAG_HAS_PLTE)))
+            return false;
+
+         if (!rpng_realloc_idat(&rpng->idat_buf, chunk_size))
+            return false;
+
+         buf += 8;
+
+         for (i = 0; i < chunk_size; i++)
+            rpng->idat_buf.data[i + rpng->idat_buf.size] = buf[i];
+
+         rpng->idat_buf.size += chunk_size;
+
+         rpng->flags         |= RPNG_FLAG_HAS_IDAT;
+         break;
+
+      case PNG_CHUNK_IEND:
+         if (     !(rpng->flags & RPNG_FLAG_HAS_IHDR) 
+               || !(rpng->flags & RPNG_FLAG_HAS_IDAT))
+            return false;
+
+         rpng->flags         |= RPNG_FLAG_HAS_IEND;
+         return false;
+   }
+
+   rpng->buff_data += chunk_size + 12;
+
+   /* Check whether data buffer pointer is valid */
+   if (rpng->buff_data > rpng->buff_end)
+      return false;
+   return true;
+}
+
+int rpng_process_image(rpng_t *rpng,
+      void **_data, size_t size, unsigned *width, unsigned *height)
+{
+   uint32_t **data = (uint32_t**)_data;
+
+   if (!rpng->process)
+   {
+      struct rpng_process *process = rpng_process_init(rpng);
+
+      if (!process)
+         goto error;
+
+      rpng->process = process;
+      return IMAGE_PROCESS_NEXT;
+   }
+
+   if (!(rpng->process->flags & RPNG_PROCESS_FLAG_INFLATE_INITIALIZED))
+   {
+      if (rpng_load_image_argb_process_inflate_init(rpng, data) == -1)
+         goto error;
+      return IMAGE_PROCESS_NEXT;
+   }
+
+   *width  = rpng->ihdr.width;
+   *height = rpng->ihdr.height;
+
+   if (rpng->ihdr.interlace && rpng->process)
+      return rpng_reverse_filter_adam7(data, &rpng->ihdr, rpng->process);
+   return rpng_reverse_filter_regular_iterate(data, &rpng->ihdr, rpng->process);
+
+error:
+   if (rpng->process)
+   {
+      if (rpng->process->inflate_buf)
+         free(rpng->process->inflate_buf);
+      if (rpng->process->stream)
+         rpng->process->stream_backend->stream_free(rpng->process->stream);
+      free(rpng->process);
+      rpng->process = NULL;
+   }
+   return IMAGE_PROCESS_ERROR;
+}
+
+void rpng_free(rpng_t *rpng)
+{
+   if (!rpng)
+      return;
+
+   if (rpng->idat_buf.data)
+      free(rpng->idat_buf.data);
+   if (rpng->process)
+   {
+      if (rpng->process->inflate_buf)
+         free(rpng->process->inflate_buf);
+      if (rpng->process->stream)
+      {
+         if (rpng->process->stream_backend && rpng->process->stream_backend->stream_free)
+            rpng->process->stream_backend->stream_free(rpng->process->stream);
+         else
+            free(rpng->process->stream);
+      }
+      free(rpng->process);
+   }
+
+   free(rpng);
+}
+
+bool rpng_start(rpng_t *rpng)
+{
+   if (!rpng)
+      return false;
+
+   /* Check whether reading the header will overflow
+    * the data buffer */
+   if (rpng->buff_end - rpng->buff_data < 8)
+      return false;
+
+   if (string_is_not_equal_fast(
+            rpng->buff_data, png_magic, sizeof(png_magic)))
+      return false;
+
+   rpng->buff_data += 8;
+
+   return true;
+}
+
+/**
+ * rpng_is_valid:
+ *
+ * Check if @rpng is a valid PNG image.
+ * Must contain an IHDR chunk, one or more IDAT
+ * chunks, and an IEND chunk.
+ *
+ * Leaf function.
+ *
+ * @return true if it's a valid PNG image, otherwise false.
+ **/
+bool rpng_is_valid(rpng_t *rpng)
+{
+   return (rpng && ((rpng->flags & (RPNG_FLAG_HAS_IHDR | RPNG_FLAG_HAS_IDAT |
+RPNG_FLAG_HAS_IEND)) > 0));
+}
+
+bool rpng_set_buf_ptr(rpng_t *rpng, void *data, size_t len)
+{
+   if (!rpng || (len < 1))
+      return false;
+
+   rpng->buff_data = (uint8_t*)data;
+   rpng->buff_end  = rpng->buff_data + (len - 1);
+
+   return true;
+}
+
+rpng_t *rpng_alloc(void)
+{
+   rpng_t *rpng = (rpng_t*)calloc(1, sizeof(*rpng));
+   if (!rpng)
+      return NULL;
+   return rpng;
+}
diff --git a/platform/libretro/libretro-common/formats/png/rpng_internal.h b/platform/libretro/libretro-common/formats/png/rpng_internal.h
new file mode 100644 (file)
index 0000000..9e5d78f
--- /dev/null
@@ -0,0 +1,49 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (rpng_internal.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _RPNG_COMMON_H
+#define _RPNG_COMMON_H
+
+#include <stdint.h>
+#include <filters.h>
+#include <formats/rpng.h>
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+static const uint8_t png_magic[8] = {
+   0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a,
+};
+
+struct png_ihdr
+{
+   uint32_t width;
+   uint32_t height;
+   uint8_t depth;
+   uint8_t color_type;
+   uint8_t compression;
+   uint8_t filter;
+   uint8_t interlace;
+};
+
+#endif
index 452763f..d410cc4 100644 (file)
@@ -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}"
- **/
-void 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'.
+ * Hidden non-leaf function cost:
+ * - Calls time
+ * - Calls rtime_localtime()
+ * - Calls strlcpy 2x
+ * - Calls string_is_empty()
+ * - Calls strftime
+ * - Calls strlcat
  *
- * 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);
+ * @return Length of the string copied into @out_path
+ **/
+size_t fill_str_dated_filename(char *out_filename,
+      const char *in_str, const char *ext, 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);
@@ -374,30 +466,51 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
  * @size               : size of output path
  *
  * Joins a directory (@dir) and path (@path) together.
- * Makes sure not to get  two consecutive slashes
+ * 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);
-
-/**
- * fill_short_pathname_representation:
- * @out_rep            : output representation
- * @in_path            : input path
- * @size               : size of output representation
- *
- * 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.
- *
- * 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,
+size_t fill_pathname_expand_special(char *out_path,
       const char *in_path, size_t size);
 
-void fill_pathname_abbreviate_special(char *out_path,
+size_t 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);
+/**
+ * fill_pathname_abbreviated_or_relative:
+ *
+ * Fills the supplied path with either the abbreviated path or 
+ * the relative path, which ever one has less depth / number of slashes
+ * 
+ * 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);
diff --git a/platform/libretro/libretro-common/include/filters.h b/platform/libretro/libretro-common/include/filters.h
new file mode 100644 (file)
index 0000000..6ef9ace
--- /dev/null
@@ -0,0 +1,103 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (filters.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _LIBRETRO_SDK_FILTERS_H
+#define _LIBRETRO_SDK_FILTERS_H
+
+/* for MSVC; should be benign under any circumstances */
+#define _USE_MATH_DEFINES
+
+#include <stdlib.h>
+#include <math.h>
+#include <retro_inline.h>
+#include <retro_math.h>
+
+/**
+ * sinc:
+ *
+ * Pure function.
+ **/
+static INLINE double sinc(double val)
+{
+   if (fabs(val) < 0.00001)
+      return 1.0;
+   return sin(val) / val;
+}
+
+/**
+ * paeth:
+ *
+ * Pure function.
+ * Paeth prediction filter.
+ **/
+static INLINE int paeth(int a, int b, int c)
+{
+   int p  = a + b - c;
+   int pa = abs(p - a);
+   int pb = abs(p - b);
+   int pc = abs(p - c);
+
+   if (pa <= pb && pa <= pc)
+      return a;
+   else if (pb <= pc)
+      return b;
+   return c;
+}
+
+/**
+ * besseli0:
+ *
+ * Pure function.
+ *
+ * Modified Bessel function of first order.
+ * Check Wiki for mathematical definition ...
+ **/
+static INLINE double besseli0(double x)
+{
+   int i;
+   double sum            = 0.0;
+   double factorial      = 1.0;
+   double factorial_mult = 0.0;
+   double x_pow          = 1.0;
+   double two_div_pow    = 1.0;
+   double x_sqr          = x * x;
+
+   /* Approximate. This is an infinite sum.
+    * Luckily, it converges rather fast. */
+   for (i = 0; i < 18; i++)
+   {
+      sum            += x_pow * two_div_pow / (factorial * factorial);
+      factorial_mult += 1.0;
+      x_pow          *= x_sqr;
+      two_div_pow    *= 0.25;
+      factorial      *= factorial_mult;
+   }
+
+   return sum;
+}
+
+static INLINE double kaiser_window_function(double index, double beta)
+{
+   return besseli0(beta * sqrtf(1 - index * index));
+}
+
+#endif
diff --git a/platform/libretro/libretro-common/include/formats/image.h b/platform/libretro/libretro-common/include/formats/image.h
new file mode 100644 (file)
index 0000000..70e5842
--- /dev/null
@@ -0,0 +1,102 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (image.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __RARCH_IMAGE_CONTEXT_H
+#define __RARCH_IMAGE_CONTEXT_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include <retro_common_api.h>
+
+#include <boolean.h>
+
+RETRO_BEGIN_DECLS
+
+enum image_process_code
+{
+   IMAGE_PROCESS_ERROR     = -2,
+   IMAGE_PROCESS_ERROR_END = -1,
+   IMAGE_PROCESS_NEXT      =  0,
+   IMAGE_PROCESS_END       =  1
+};
+
+struct texture_image
+{
+   uint32_t *pixels;
+   unsigned width;
+   unsigned height;
+   bool supports_rgba;
+};
+
+enum image_type_enum
+{
+   IMAGE_TYPE_NONE = 0,
+   IMAGE_TYPE_PNG,
+   IMAGE_TYPE_JPEG,
+   IMAGE_TYPE_BMP,
+   IMAGE_TYPE_TGA
+};
+
+enum image_type_enum image_texture_get_type(const char *path);
+
+bool image_texture_set_color_shifts(unsigned *r_shift, unsigned *g_shift,
+      unsigned *b_shift, unsigned *a_shift,
+      struct texture_image *out_img);
+
+bool image_texture_color_convert(unsigned r_shift,
+      unsigned g_shift, unsigned b_shift, unsigned a_shift,
+      struct texture_image *out_img);
+
+bool image_texture_load_buffer(struct texture_image *img,
+   enum image_type_enum type, void *buffer, size_t buffer_len);
+
+bool image_texture_load(struct texture_image *img, const char *path);
+void image_texture_free(struct texture_image *img);
+
+/* Image transfer */
+
+void image_transfer_free(void *data, enum image_type_enum type);
+
+void *image_transfer_new(enum image_type_enum type);
+
+bool image_transfer_start(void *data, enum image_type_enum type);
+
+void image_transfer_set_buffer_ptr(
+      void *data,
+      enum image_type_enum type,
+      void *ptr,
+      size_t len);
+
+int image_transfer_process(
+      void *data,
+      enum image_type_enum type,
+      uint32_t **buf, size_t size,
+      unsigned *width, unsigned *height);
+
+bool image_transfer_iterate(void *data, enum image_type_enum type);
+
+bool image_transfer_is_valid(void *data, enum image_type_enum type);
+
+RETRO_END_DECLS
+
+#endif
diff --git a/platform/libretro/libretro-common/include/formats/rpng.h b/platform/libretro/libretro-common/include/formats/rpng.h
new file mode 100644 (file)
index 0000000..d855658
--- /dev/null
@@ -0,0 +1,64 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (rpng.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __LIBRETRO_SDK_FORMAT_RPNG_H__
+#define __LIBRETRO_SDK_FORMAT_RPNG_H__
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include <retro_common_api.h>
+
+#include <boolean.h>
+
+RETRO_BEGIN_DECLS
+
+typedef struct rpng rpng_t;
+
+rpng_t *rpng_init(const char *path);
+
+bool rpng_is_valid(rpng_t *rpng);
+
+bool rpng_set_buf_ptr(rpng_t *rpng, void *data, size_t len);
+
+rpng_t *rpng_alloc(void);
+
+void rpng_free(rpng_t *rpng);
+
+bool rpng_iterate_image(rpng_t *rpng);
+
+int rpng_process_image(rpng_t *rpng,
+      void **data, size_t size, unsigned *width, unsigned *height);
+
+bool rpng_start(rpng_t *rpng);
+
+bool rpng_save_image_argb(const char *path, const uint32_t *data,
+      unsigned width, unsigned height, unsigned pitch);
+bool rpng_save_image_bgr24(const char *path, const uint8_t *data,
+      unsigned width, unsigned height, unsigned pitch);
+
+uint8_t* rpng_save_image_bgr24_string(const uint8_t *data,
+      unsigned width, unsigned height, signed pitch, uint64_t *bytes);
+
+RETRO_END_DECLS
+
+#endif
diff --git a/platform/libretro/libretro-common/include/retro_math.h b/platform/libretro/libretro-common/include/retro_math.h
new file mode 100644 (file)
index 0000000..d1cb862
--- /dev/null
@@ -0,0 +1,190 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (retro_math.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _LIBRETRO_COMMON_MATH_H
+#define _LIBRETRO_COMMON_MATH_H
+
+#include <stdint.h>
+
+#if defined(_WIN32) && !defined(_XBOX)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#elif defined(_WIN32) && defined(_XBOX)
+#include <Xtl.h>
+#endif
+
+#include <limits.h>
+
+#ifdef _MSC_VER
+#include <compat/msvc.h>
+#endif
+#include <retro_inline.h>
+
+#ifndef M_PI
+#if !defined(USE_MATH_DEFINES)
+#define M_PI 3.14159265358979323846264338327
+#endif
+#endif
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/**
+ * next_pow2:
+ * @v         : initial value
+ *
+ * Get next power of 2 value based on  initial value.
+ *
+ * Returns: next power of 2 value (derived from @v).
+ **/
+static INLINE uint32_t next_pow2(uint32_t v)
+{
+   v--;
+   v |= v >> 1;
+   v |= v >> 2;
+   v |= v >> 4;
+   v |= v >> 8;
+   v |= v >> 16;
+   v++;
+   return v;
+}
+
+/**
+ * prev_pow2:
+ * @v         : initial value
+ *
+ * Get previous power of 2 value based on initial value.
+ *
+ * Returns: previous power of 2 value (derived from @v).
+ **/
+static INLINE uint32_t prev_pow2(uint32_t v)
+{
+   v |= v >> 1;
+   v |= v >> 2;
+   v |= v >> 4;
+   v |= v >> 8;
+   v |= v >> 16;
+   return v - (v >> 1);
+}
+
+/**
+ * clamp:
+ * @v         : initial value
+ *
+ * Get the clamped value based on initial value.
+ *
+ * Returns: clamped value (derived from @v).
+ **/
+static INLINE float clamp_value(float v, float min, float max)
+{
+   return v <= min ? min : v >= max ? max : v;
+}
+
+/**
+ * saturate_value:
+ * @v         : initial value
+ *
+ * Get the clamped 0.0-1.0 value based on initial value.
+ *
+ * Returns: clamped 0.0-1.0 value (derived from @v).
+ **/
+static INLINE float saturate_value(float v)
+{
+   return clamp_value(v, 0.0f, 1.0f);
+}
+
+/**
+ * dot_product:
+ * @a         : left hand vector value
+ * @b         : right hand vector value
+ *
+ * Get the dot product of the two passed in vectors.
+ *
+ * Returns: dot product value (derived from @a and @b).
+ **/
+static INLINE float dot_product(const float* a, const float* b) 
+{
+   return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
+}
+
+/**
+ * convert_rgb_to_yxy:
+ * @rgb         : in RGB colour space value
+ * @Yxy         : out Yxy colour space value
+ *
+ * Convert from RGB colour space to Yxy colour space.
+ *
+ * Returns: Yxy colour space value (derived from @rgb).
+ **/
+static INLINE void convert_rgb_to_yxy(const float* rgb, float* Yxy) 
+{
+   float inv;
+   float xyz[3];
+   float one[3]        = {1.0, 1.0, 1.0};
+   float rgb_xyz[3][3] = {
+      {0.4124564, 0.3575761, 0.1804375},
+      {0.2126729, 0.7151522, 0.0721750},
+      {0.0193339, 0.1191920, 0.9503041}
+   };
+
+   xyz[0]              = dot_product(rgb_xyz[0], rgb);
+   xyz[1]              = dot_product(rgb_xyz[1], rgb);
+   xyz[2]              = dot_product(rgb_xyz[2], rgb);
+
+   inv                 = 1.0f / dot_product(xyz, one);
+   Yxy[0]              = xyz[1]; 
+   Yxy[1]              = xyz[0] * inv;
+   Yxy[2]              = xyz[1] * inv;
+}
+/**
+ * convert_yxy_to_rgb:
+ * @rgb         : in Yxy colour space value
+ * @Yxy         : out rgb colour space value
+ *
+ * Convert from Yxy colour space to rgb colour space.
+ *
+ * Returns: rgb colour space value (derived from @Yxy).
+ **/
+static INLINE void convert_yxy_to_rgb(const float* Yxy, float* rgb)
+{
+   float xyz[3];
+   float xyz_rgb[3][3] = {
+      {3.2404542, -1.5371385, -0.4985314},
+      {-0.9692660, 1.8760108,  0.0415560},
+      {0.0556434, -0.2040259, 1.0572252}
+   };
+   xyz[0]              = Yxy[0] * Yxy[1] / Yxy[2];
+   xyz[1]              = Yxy[0];
+   xyz[2]              = Yxy[0] * (1.0 - Yxy[1] - Yxy[2]) / Yxy[2];
+
+   rgb[0]              = dot_product(xyz_rgb[0], xyz);
+   rgb[1]              = dot_product(xyz_rgb[1], xyz);
+   rgb[2]              = dot_product(xyz_rgb[2], xyz);
+}
+
+#endif
diff --git a/platform/libretro/libretro-common/include/streams/trans_stream.h b/platform/libretro/libretro-common/include/streams/trans_stream.h
new file mode 100644 (file)
index 0000000..79c0570
--- /dev/null
@@ -0,0 +1,106 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (trans_stream.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef LIBRETRO_SDK_TRANS_STREAM_H__
+#define LIBRETRO_SDK_TRANS_STREAM_H__
+
+#include <stdint.h>
+#include <stddef.h>
+#include <boolean.h>
+
+#ifdef _WIN32
+#include <direct.h>
+#else
+#include <unistd.h>
+#endif
+
+#include <retro_miscellaneous.h>
+
+#include <retro_common_api.h>
+
+RETRO_BEGIN_DECLS
+
+enum trans_stream_error
+{
+    TRANS_STREAM_ERROR_NONE = 0,
+    TRANS_STREAM_ERROR_AGAIN, /* more work to do */
+    TRANS_STREAM_ERROR_ALLOCATION_FAILURE, /* malloc failure */
+    TRANS_STREAM_ERROR_INVALID, /* invalid state */
+    TRANS_STREAM_ERROR_BUFFER_FULL, /* output buffer full */
+    TRANS_STREAM_ERROR_OTHER
+};
+
+struct trans_stream_backend
+{
+   const char *ident;
+   const struct trans_stream_backend *reverse;
+
+   /* Create a stream data structure */
+   void *(*stream_new)(void);
+
+   /* Free it */
+   void  (*stream_free)(void *);
+
+   /* (Optional) Set extra properties, defined per transcoder */
+   bool  (*define)(void *, const char *, uint32_t);
+
+   /* Set our input source */
+   void  (*set_in)(void *, const uint8_t *, uint32_t);
+
+   /* Set our output target */
+   void  (*set_out)(void *, uint8_t *, uint32_t);
+
+   /* Perform a transcoding, flushing/finalizing if asked to. Writes out how
+    * many bytes were read and written. Error target optional. */
+   bool  (*trans)(void *, bool, uint32_t *, uint32_t *, enum trans_stream_error *);
+};
+
+/**
+ * trans_stream_trans_full:
+ * @backend                     : transcoding backend
+ * @data                        : (optional) existing stream data, or a target
+ *                                for the new stream data to be saved
+ * @in                          : input data
+ * @in_size                     : input size
+ * @out                         : output data
+ * @out_size                    : output size
+ * @error                       : (optional) output for error code
+ *
+ * Perform a full transcoding from a source to a destination.
+ */
+bool trans_stream_trans_full(
+    struct trans_stream_backend *backend, void **data,
+    const uint8_t *in, uint32_t in_size,
+    uint8_t *out, uint32_t out_size,
+    enum trans_stream_error *error);
+
+const struct trans_stream_backend* trans_stream_get_zlib_deflate_backend(void);
+const struct trans_stream_backend* trans_stream_get_zlib_inflate_backend(void);
+const struct trans_stream_backend* trans_stream_get_pipe_backend(void);
+
+extern const struct trans_stream_backend zlib_deflate_backend;
+extern const struct trans_stream_backend zlib_inflate_backend;
+extern const struct trans_stream_backend pipe_backend;
+
+RETRO_END_DECLS
+
+#endif
diff --git a/platform/libretro/libretro-common/streams/trans_stream.c b/platform/libretro/libretro-common/streams/trans_stream.c
new file mode 100644 (file)
index 0000000..aecb457
--- /dev/null
@@ -0,0 +1,92 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (trans_stream.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <streams/trans_stream.h>
+
+/**
+ * trans_stream_trans_full:
+ * @data                        : (optional) existing stream data, or a target
+ *                                for the new stream data to be saved
+ * @in                          : input data
+ * @in_size                     : input size
+ * @out                         : output data
+ * @out_size                    : output size
+ * @error                       : (optional) output for error code
+ *
+ * Perform a full transcoding from a source to a destination.
+ */
+bool trans_stream_trans_full(
+    struct trans_stream_backend *backend, void **data,
+    const uint8_t *in, uint32_t in_size,
+    uint8_t *out, uint32_t out_size,
+    enum trans_stream_error *error)
+{
+   void *rdata;
+   bool ret;
+   uint32_t rd, wn;
+
+   if (data && *data)
+      rdata = *data;
+   else
+   {
+      if (!(rdata = backend->stream_new()))
+      {
+         if (error)
+            *error = TRANS_STREAM_ERROR_ALLOCATION_FAILURE;
+         return false;
+      }
+   }
+
+   backend->set_in(rdata, in, in_size);
+   backend->set_out(rdata, out, out_size);
+   ret = backend->trans(rdata, true, &rd, &wn, error);
+
+   if (data)
+      *data = rdata;
+   else
+      backend->stream_free(rdata);
+
+   return ret;
+}
+
+const struct trans_stream_backend* trans_stream_get_zlib_deflate_backend(void)
+{
+#if HAVE_ZLIB
+   return &zlib_deflate_backend;
+#else
+   return NULL;
+#endif
+}
+
+const struct trans_stream_backend* trans_stream_get_zlib_inflate_backend(void)
+{
+#if HAVE_ZLIB
+   return &zlib_inflate_backend;
+#else
+   return NULL;
+#endif
+}
+
+const struct trans_stream_backend* trans_stream_get_pipe_backend(void)
+{
+   return &pipe_backend;
+}
diff --git a/platform/libretro/libretro-common/streams/trans_stream_pipe.c b/platform/libretro/libretro-common/streams/trans_stream_pipe.c
new file mode 100644 (file)
index 0000000..76f8b16
--- /dev/null
@@ -0,0 +1,111 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (trans_stream_pipe.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <streams/trans_stream.h>
+
+struct pipe_trans_stream
+{
+   const uint8_t *in;
+   uint8_t *out;
+   uint32_t in_size, out_size;
+};
+
+static void *pipe_stream_new(void)
+{
+   struct pipe_trans_stream *stream = 
+      (struct pipe_trans_stream*)malloc(sizeof(*stream));
+   if (!stream)
+      return NULL;
+
+   stream->in                       = NULL;
+   stream->out                      = NULL;
+   stream->in_size                  = 0;
+   stream->out_size                 = 0;
+
+   return stream;
+}
+
+static void pipe_stream_free(void *data)
+{
+   free(data);
+}
+
+static void pipe_set_in(void *data, const uint8_t *in, uint32_t in_size)
+{
+   struct pipe_trans_stream *p = (struct pipe_trans_stream *) data;
+
+   if (!p)
+      return;
+         
+   p->in                       = in;
+   p->in_size                  = in_size;
+}
+
+static void pipe_set_out(void *data, uint8_t *out, uint32_t out_size)
+{
+   struct pipe_trans_stream *p = (struct pipe_trans_stream *) data;
+   
+   if (!p)
+      return;
+
+   p->out                      = out;
+   p->out_size                 = out_size;
+}
+
+static bool pipe_trans(
+   void *data, bool flush,
+   uint32_t *rd, uint32_t *wn,
+   enum trans_stream_error *error)
+{
+   struct pipe_trans_stream *p = (struct pipe_trans_stream *) data;
+
+   if (p->out_size < p->in_size)
+   {
+      memcpy(p->out, p->in, p->out_size);
+      *rd     = *wn = p->out_size;
+      p->in  += p->out_size;
+      p->out += p->out_size;
+      *error  = TRANS_STREAM_ERROR_BUFFER_FULL;
+      return false;
+   }
+
+   memcpy(p->out, p->in, p->in_size);
+   *rd     = *wn = p->in_size;
+   p->in  += p->in_size;
+   p->out += p->in_size;
+   *error  = TRANS_STREAM_ERROR_NONE;
+   return true;
+}
+
+const struct trans_stream_backend pipe_backend = {
+   "pipe",
+   &pipe_backend,
+   pipe_stream_new,
+   pipe_stream_free,
+   NULL,
+   pipe_set_in,
+   pipe_set_out,
+   pipe_trans
+};
diff --git a/platform/libretro/libretro-common/streams/trans_stream_zlib.c b/platform/libretro/libretro-common/streams/trans_stream_zlib.c
new file mode 100644 (file)
index 0000000..e5d1a4a
--- /dev/null
@@ -0,0 +1,330 @@
+/* Copyright  (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (trans_stream_zlib.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <zlib.h>
+#include <string/stdstring.h>
+#include <streams/trans_stream.h>
+
+struct zlib_trans_stream
+{
+   z_stream z;
+   int ex; /* window_bits or level */
+   bool inited;
+};
+
+static void *zlib_deflate_stream_new(void)
+{
+   struct zlib_trans_stream *ret = (struct zlib_trans_stream*)
+      malloc(sizeof(*ret));
+   if (!ret)
+      return NULL;
+   ret->inited      = false;
+   ret->ex          = 9;
+
+   ret->z.next_in   = NULL;
+   ret->z.avail_in  = 0;
+   ret->z.total_in  = 0;
+   ret->z.next_out  = NULL;
+   ret->z.avail_out = 0;
+   ret->z.total_out = 0;
+
+   ret->z.msg       = NULL;
+   ret->z.state     = NULL;
+
+   ret->z.zalloc    = NULL;
+   ret->z.zfree     = NULL;
+   ret->z.opaque    = NULL;
+
+   ret->z.data_type = 0;
+   ret->z.adler     = 0;
+   ret->z.reserved  = 0;
+   return (void *)ret;
+}
+
+static void *zlib_inflate_stream_new(void)
+{
+   struct zlib_trans_stream *ret = (struct zlib_trans_stream*)
+      malloc(sizeof(*ret));
+   if (!ret)
+      return NULL;
+   ret->inited      = false;
+   ret->ex          = MAX_WBITS;
+
+   ret->z.next_in   = NULL;
+   ret->z.avail_in  = 0;
+   ret->z.total_in  = 0;
+   ret->z.next_out  = NULL;
+   ret->z.avail_out = 0;
+   ret->z.total_out = 0;
+
+   ret->z.msg       = NULL;
+   ret->z.state     = NULL;
+
+   ret->z.zalloc    = NULL;
+   ret->z.zfree     = NULL;
+   ret->z.opaque    = NULL;
+
+   ret->z.data_type = 0;
+   ret->z.adler     = 0;
+   ret->z.reserved  = 0;
+   return (void *)ret;
+}
+
+static void zlib_deflate_stream_free(void *data)
+{
+   struct zlib_trans_stream *z = (struct zlib_trans_stream *) data;
+   if (!z)
+      return;
+   if (z->inited)
+      deflateEnd(&z->z);
+   free(z);
+}
+
+static void zlib_inflate_stream_free(void *data)
+{
+   struct zlib_trans_stream *z = (struct zlib_trans_stream *) data;
+   if (!z)
+      return;
+   if (z->inited)
+      inflateEnd(&z->z);
+   if (z)
+      free(z);
+}
+
+static bool zlib_deflate_define(void *data, const char *prop, uint32_t val)
+{
+   struct zlib_trans_stream *z = (struct zlib_trans_stream *) data;
+   if (string_is_equal(prop, "level"))
+   {
+      if (z)
+         z->ex = (int) val;
+      return true;
+   }
+   return false;
+}
+
+static bool zlib_inflate_define(void *data, const char *prop, uint32_t val)
+{
+   struct zlib_trans_stream *z = (struct zlib_trans_stream *) data;
+   if (string_is_equal(prop, "window_bits"))
+   {
+      if (z)
+         z->ex = (int) val;
+      return true;
+   }
+   return false;
+}
+
+static void zlib_deflate_set_in(void *data, const uint8_t *in, uint32_t in_size)
+{
+   struct zlib_trans_stream *z = (struct zlib_trans_stream *) data;
+
+   if (!z)
+      return;
+
+   z->z.next_in                = (uint8_t *) in;
+   z->z.avail_in               = in_size;
+
+   if (!z->inited)
+   {
+      deflateInit(&z->z, z->ex);
+      z->inited = true;
+   }
+}
+
+static void zlib_inflate_set_in(void *data, const uint8_t *in, uint32_t in_size)
+{
+   struct zlib_trans_stream *z = (struct zlib_trans_stream *) data;
+
+   if (!z)
+      return;
+
+   z->z.next_in                = (uint8_t *) in;
+   z->z.avail_in               = in_size;
+   if (!z->inited)
+   {
+      inflateInit2(&z->z, z->ex);
+      z->inited = true;
+   }
+}
+
+static void zlib_set_out(void *data, uint8_t *out, uint32_t out_size)
+{
+   struct zlib_trans_stream *z = (struct zlib_trans_stream *) data;
+
+   if (!z)
+      return;
+
+   z->z.next_out               = out;
+   z->z.avail_out              = out_size;
+}
+
+static bool zlib_deflate_trans(
+   void *data, bool flush,
+   uint32_t *rd, uint32_t *wn,
+   enum trans_stream_error *error)
+{
+   int zret                     = 0;
+   bool ret                     = false;
+   uint32_t pre_avail_in        = 0;
+   uint32_t pre_avail_out       = 0;
+   struct zlib_trans_stream *zt = (struct zlib_trans_stream *) data;
+   z_stream                  *z = &zt->z;
+
+   if (!zt->inited)
+   {
+      deflateInit(z, zt->ex);
+      zt->inited = true;
+   }
+
+   pre_avail_in  = z->avail_in;
+   pre_avail_out = z->avail_out;
+   zret          = deflate(z, flush ? Z_FINISH : Z_NO_FLUSH);
+
+   if (zret == Z_OK)
+   {
+      if (error)
+         *error = TRANS_STREAM_ERROR_AGAIN;
+   }
+   else if (zret == Z_STREAM_END)
+   {
+      if (error)
+         *error = TRANS_STREAM_ERROR_NONE;
+   }
+   else
+   {
+      if (error)
+         *error = TRANS_STREAM_ERROR_OTHER;
+      return false;
+   }
+   ret = true;
+
+   if (z->avail_out == 0)
+   {
+      /* Filled buffer, maybe an error */
+      if (z->avail_in != 0)
+      {
+         ret = false;
+         if (error)
+            *error = TRANS_STREAM_ERROR_BUFFER_FULL;
+      }
+   }
+
+   *rd = pre_avail_in - z->avail_in;
+   *wn = pre_avail_out - z->avail_out;
+
+   if (flush && zret == Z_STREAM_END)
+   {
+      deflateEnd(z);
+      zt->inited = false;
+   }
+
+   return ret;
+}
+
+static bool zlib_inflate_trans(
+   void *data, bool flush,
+   uint32_t *rd, uint32_t *wn,
+   enum trans_stream_error *error)
+{
+   int zret;
+   bool ret                     = false;
+   uint32_t pre_avail_in        = 0;
+   uint32_t pre_avail_out       = 0;
+   struct zlib_trans_stream *zt = (struct zlib_trans_stream *) data;
+   z_stream                  *z = &zt->z;
+
+   if (!zt->inited)
+   {
+      inflateInit2(z, zt->ex);
+      zt->inited = true;
+   }
+
+   pre_avail_in  = z->avail_in;
+   pre_avail_out = z->avail_out;
+   zret          = inflate(z, flush ? Z_FINISH : Z_NO_FLUSH);
+
+   if (zret == Z_OK)
+   {
+      if (error)
+         *error = TRANS_STREAM_ERROR_AGAIN;
+   }
+   else if (zret == Z_STREAM_END)
+   {
+      if (error)
+         *error = TRANS_STREAM_ERROR_NONE;
+   }
+   else
+   {
+      if (error)
+         *error = TRANS_STREAM_ERROR_OTHER;
+      return false;
+   }
+   ret = true;
+
+   if (z->avail_out == 0)
+   {
+      /* Filled buffer, maybe an error */
+      if (z->avail_in != 0)
+      {
+         ret = false;
+         if (error)
+            *error = TRANS_STREAM_ERROR_BUFFER_FULL;
+      }
+   }
+
+   *rd = pre_avail_in - z->avail_in;
+   *wn = pre_avail_out - z->avail_out;
+
+   if (flush && zret == Z_STREAM_END)
+   {
+      inflateEnd(z);
+      zt->inited = false;
+   }
+
+   return ret;
+}
+
+const struct trans_stream_backend zlib_deflate_backend = {
+   "zlib_deflate",
+   &zlib_inflate_backend,
+   zlib_deflate_stream_new,
+   zlib_deflate_stream_free,
+   zlib_deflate_define,
+   zlib_deflate_set_in,
+   zlib_set_out,
+   zlib_deflate_trans
+};
+
+const struct trans_stream_backend zlib_inflate_backend = {
+   "zlib_inflate",
+   &zlib_deflate_backend,
+   zlib_inflate_stream_new,
+   zlib_inflate_stream_free,
+   zlib_inflate_define,
+   zlib_inflate_set_in,
+   zlib_set_out,
+   zlib_inflate_trans
+};
index 26b89ac..6423a61 100644 (file)
 #include <libkern/OSCacheControl.h>
 #endif
 
+#include "libretro-common/include/formats/image.h" // really, for IMAGE_PROCESS_NEXT?!?
+#include "libretro-common/include/formats/rpng.h"
+#include "libretro-common/include/file/file_path.h"
+
 #include "libretro-common/include/memmap.h"
 /* Ouf, libretro-common defines  replacement functions, but not the flags :-| */
 #ifndef PROT_READ
@@ -39,6 +43,7 @@
 #include <platform/common/upscale.h>
 #endif
 #include <platform/common/emu.h>
+#include <platform/libpicofe/plat.h> // need this for PXMAKE in readpng :-/
 
 #ifdef _3DS
 #include "3ds/3ds_utils.h"
@@ -154,8 +159,14 @@ static bool retro_audio_buff_underrun      = false;
 static unsigned audio_latency              = 0;
 static bool update_audio_latency           = false;
 static uint16_t pico_events;
-int pico_inp_mode, pico_pen_visible;
+// Sega Pico stuff
+int pico_inp_mode;
 int pico_pen_x = 320/2, pico_pen_y = 240/2;
+static int pico_page;
+static int pico_w, pico_h;
+static char pico_overlay_path[PATH_MAX];
+static unsigned short *pico_overlay;
+
 
 static void retro_audio_buff_status_cb(
       bool active, unsigned occupancy, bool underrun_likely)
@@ -1284,7 +1295,7 @@ static const char *find_bios(int *region, const char *cd_fname)
       f = fopen(path, "rb");
       if (f != NULL) {
          log_cb(RETRO_LOG_INFO, "found MSU rom: %s\n", path);
-        fclose(f);
+         fclose(f);
          return path;
       }
    }
@@ -1454,11 +1465,11 @@ bool retro_load_game(const struct retro_game_info *info)
       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right (green)" },
       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Red Button" },
       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Pen Button" },
-      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Switch input" },
-      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Pen sensor" },
-      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Pen visibility" },
-      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "Previous page" },
-      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "Next page" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Pen State" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Pen on Storyware" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Pen on Pad" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "Previous Page" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "Next Page" },
 
       { 0 },
    };
@@ -1598,6 +1609,7 @@ bool retro_load_game(const struct retro_game_info *info)
       break;
    }
 
+   strncpy(pico_overlay_path, content_path, sizeof(pico_overlay_path)-4);
    if (PicoIn.AHW & PAHW_PICO)
       environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc_pico);
    else if (PicoIn.AHW & PAHW_SMS)
@@ -2060,34 +2072,160 @@ void emu_status_msg(const char *format, ...)
 static void draw_pico_ptr(void)
 {
    int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;
-   int x, y, pitch = vout_width, offs;
+   int x, y, pitch = vout_width;
    unsigned short *p = (unsigned short *)((char *)vout_buf + vout_offset);
    int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);
+   // storyware pages are actually squished, 2:1
+   int h = (pico_inp_mode == 1 ? 160 : vout_height);
+   if (h < 224) y++;
 
-   x = ((pico_pen_x * vout_width  * ((1ULL<<32) / 320 + 1)) >> 32);
-   y = ((pico_pen_y * vout_height * ((1ULL<<32) / 224 + 1)) >> 32);
+   x = ((pico_pen_x * vout_width * ((1ULL<<32) / 320 + 1)) >> 32);
+   y = ((pico_pen_y * h          * ((1ULL<<32) / 224 + 1)) >> 32);
    p += x + y * pitch;
 
-   p[-pitch-1] ^= _; p[-pitch] ^= o; p[-pitch+1] ^= _;
-   p[-1]       ^= o; p[0]      ^= o; p[1]        ^= o;
-   p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= _;
+   p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;
+   p[-1]       ^= _; p[0]      ^= o; p[1]        ^= o; p[2]        ^= _;
+   p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= o; p[pitch+2]  ^= _;
+   p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;
+}
+
+static int readpng(unsigned short *dest, const char *fname, int req_w, int req_h)
+{
+   rpng_t *rpng = rpng_alloc();
+   FILE *pf = fopen(fname, "rb");
+   void *png = NULL, *img = NULL;
+   size_t len;
+   unsigned int x, y, w = req_w, h = req_h;
+   int ret = -1;
+
+   if (!rpng || !pf) {
+      lprintf("can't read png file %s", fname);
+      goto done;
+   }
+   // who designed this, reading the whole file for inflating, really?
+   fseek(pf, 0, SEEK_END);
+   len = ftell(pf);
+   fseek(pf, 0, SEEK_SET);
+   if (!(png = malloc(len))) {
+      lprintf("oom while reading png file %s", fname);
+      goto done;
+   }
+   fread(png, 1, len, pf);
+
+   // again, who designed this? why all this superfluous iterating here?
+   rpng_set_buf_ptr(rpng, png, len);
+   rpng_start(rpng);
+   while (rpng_iterate_image(rpng));
+   do {
+      ret = rpng_process_image(rpng, &img, len, &w, &h);
+   } while (ret == IMAGE_PROCESS_NEXT);
+
+   // there's already a scaled pngread in libpicofe, but who cares :-/
+   if (img && rpng_is_valid(rpng)) {
+      int x_scale = w*65536 / req_w;
+      int y_scale = h*65536 / req_h;
+      int x_ofs, y_ofs, x_pos, y_pos;
+
+      if (x_scale < y_scale)
+            x_scale = y_scale;
+      else  y_scale = x_scale;
+      x_ofs = req_w - w*65536 / x_scale;
+      y_ofs = req_h - h*65536 / y_scale;
+
+      dest += y_ofs/2*req_w + x_ofs/2;
+      for (y_pos = 0; y_pos < h*65536; y_pos += y_scale+1)
+      {
+         unsigned char *src = (unsigned char *)img + 4*w*(y_pos >> 16);
+         for (x_pos = 0, len = 0; x_pos < w*65536; x_pos += x_scale+1, len++)
+         {
+            // to add insult to injury, rpng writes the image endian dependant!
+            unsigned int d = *(unsigned int *)&src[4*(x_pos >> 16)];
+            int r = d >> 16, g = d >> 8, b = d;
+            *dest++ = PXMAKE(r & 0xff, g & 0xff, b & 0xff);
+         }
+         dest += req_w - len;
+      }
+   }
+   ret = 0;
+
+done:
+   if (img) free(img);
+   if (png) free(png);
+   if (pf) fclose(pf);
+   rpng_free(rpng);
+   return ret;
+}
+
+static unsigned short *load_pico_overlay(int page, int w, int h)
+{
+   static const char *pic_exts[] = { "png", "PNG" };
+   char buffer[PATH_MAX];
+   char *ext, *fname = NULL;
+   int extpos, i;
+
+   if (pico_page == page && pico_w == w && pico_h == h)
+      return pico_overlay;
+   pico_page = page;
+   pico_w = w, pico_h = h;
+
+   ext = strrchr(pico_overlay_path, '.');
+   extpos = ext ? ext-pico_overlay_path : strlen(pico_overlay_path);
+   strcpy(buffer, pico_overlay_path);
+   buffer[extpos++] = '_';
+   if (page < 0) {
+      buffer[extpos++] = 'p';
+      buffer[extpos++] = 'a';
+      buffer[extpos++] = 'd';
+   } else
+      buffer[extpos++] = '0'+PicoPicohw.page;
+   buffer[extpos++] = '.';
+
+   for (i = 0; i < ARRAY_SIZE(pic_exts); i++) {
+      strcpy(buffer+extpos, pic_exts[i]);
+      if (path_is_valid(buffer) == RETRO_VFS_STAT_IS_VALID) {
+         printf("found Pico file: %s\n", buffer);
+         fname = buffer;
+         break;
+      }
+   }
+
+   pico_overlay = realloc(pico_overlay, w*h*2);
+   memset(pico_overlay, 0, w*h*2);
+   if (!fname || !pico_overlay || readpng(pico_overlay, fname, w, h)) {
+      if (pico_overlay)
+         free(pico_overlay);
+      pico_overlay = NULL;
+   }
+
+   return pico_overlay;
+}
+
+void emu_pico_overlay(u16 *pd, int w, int h, int pitch)
+{
+       u16 *overlay = NULL;
+       int y, oh = h;
+
+       // get overlay
+       if (pico_inp_mode == 1) {
+               oh = (w/2 < h ? w/2 : h); // storyware has squished h
+               overlay = load_pico_overlay(PicoPicohw.page, w, oh);
+       } else if (pico_inp_mode == 2)
+               overlay = load_pico_overlay(-1, w, oh);
+
+       // copy overlay onto buffer
+       if (overlay) {
+               for (y = 0; y < oh; y++)
+                       memcpy(pd + y*pitch, overlay + y*w, w*2);
+               if (y < h)
+                       memset(pd + y*pitch, 0, w*2);
+       }
 }
 
 void run_events_pico(unsigned int events)
 {
     int lim_x;
 
-    if (events & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT)) {
-       pico_inp_mode++;
-       if (pico_inp_mode > 2)
-           pico_inp_mode = 0;
-       switch (pico_inp_mode) {
-       case 2: emu_status_msg("Input: Pen on Pad"); break;
-       case 1: emu_status_msg("Input: Pen on Storyware"); break;
-       case 0: emu_status_msg("Input: Joystick"); break;
-       }
-       PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;
-    }
     if (events & (1 << RETRO_DEVICE_ID_JOYPAD_L)) {
        PicoPicohw.page--;
        if (PicoPicohw.page < 0)
@@ -2101,8 +2239,22 @@ void run_events_pico(unsigned int events)
        emu_status_msg("Page %i", PicoPicohw.page);
     }
     if (events & (1 << RETRO_DEVICE_ID_JOYPAD_X)) {
-        pico_pen_visible = !pico_pen_visible;
-        emu_status_msg("%s Pen", pico_pen_visible ? "Show" : "Hide");
+        if (pico_inp_mode == 2) {
+            pico_inp_mode = 0;
+            emu_status_msg("Input: D-Pad");
+        } else {
+            pico_inp_mode = 2;
+            emu_status_msg("Input: Pen on Pad");
+        }
+    }
+    if (events & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT)) {
+        if (pico_inp_mode == 1) {
+            pico_inp_mode = 0;
+            emu_status_msg("Input: D-Pad");
+        } else {
+            pico_inp_mode = 1;
+            emu_status_msg("Input: Pen on Storyware");
+        }
     }
     if (events & (1 << RETRO_DEVICE_ID_JOYPAD_START)) {
         PicoPicohw.pen_pos[0] ^= 0x8000;
@@ -2110,6 +2262,10 @@ void run_events_pico(unsigned int events)
         emu_status_msg("Pen %s", PicoPicohw.pen_pos[0] & 0x8000 ? "Up" : "Down");
     }
 
+    if ((PicoIn.pad[0] & 0x20) && pico_inp_mode && pico_overlay) {
+        pico_inp_mode = 0;
+        emu_status_msg("Input: D-Pad");
+    }
     if (pico_inp_mode == 0)
        return;
 
@@ -2122,12 +2278,12 @@ void run_events_pico(unsigned int events)
 
     if (pico_pen_y < PICO_PEN_ADJUST_Y)
        pico_pen_y = PICO_PEN_ADJUST_Y;
-    if (pico_pen_y > 224-1 - PICO_PEN_ADJUST_Y)
-       pico_pen_y = 224-1 - PICO_PEN_ADJUST_Y;
+    if (pico_pen_y > 223-1 - PICO_PEN_ADJUST_Y)
+       pico_pen_y = 223-1 - PICO_PEN_ADJUST_Y;
     if (pico_pen_x < PICO_PEN_ADJUST_X)
        pico_pen_x = PICO_PEN_ADJUST_X;
-    if (pico_pen_x > 320-1 - PICO_PEN_ADJUST_X)
-       pico_pen_x = 320-1 - PICO_PEN_ADJUST_X;
+    if (pico_pen_x > 319-1 - PICO_PEN_ADJUST_X)
+       pico_pen_x = 319-1 - PICO_PEN_ADJUST_X;
 
     PicoPicohw.pen_pos[0] &= 0x8000;
     PicoPicohw.pen_pos[1] &= 0x8000;
@@ -2332,8 +2488,15 @@ void retro_run(void)
       }
    }
 
-   if ((PicoIn.AHW & PAHW_PICO) && pico_pen_visible)
-      if (pico_inp_mode) draw_pico_ptr();
+   if (PicoIn.AHW & PAHW_PICO) {
+      int h = vout_height, w = vout_width;
+      unsigned short *pd = (unsigned short *)((char *)vout_buf + vout_offset);
+
+      if (pico_inp_mode)
+         emu_pico_overlay(pd, w, h, vout_width);
+      if (pico_inp_mode /*== 2 || overlay*/)
+         draw_pico_ptr();
+   }
 
    buff = (char*)vout_buf + vout_offset;
 #endif
@@ -2445,9 +2608,13 @@ void retro_deinit(void)
    free(vout_buf);
 #endif
    vout_buf = NULL;
+
    if (vout_ghosting_buf)
       free(vout_ghosting_buf);
    vout_ghosting_buf = NULL;
+   if (pico_overlay)
+      free(pico_overlay);
+   pico_overlay = NULL;
 
    libretro_supports_bitmasks = false;
 }