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)" ""
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
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 \
#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)
{
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)
* 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
* 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)
*
* 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;
}
* 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;
}
/**
* 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;
}
/**
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;
}
/**
* @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);
}
/**
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
*
* 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);
*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;
}
/**
*
* 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);
}
/**
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;
}
/**
*
* 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;
}
/**
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);
*
* 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 */
*
* 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;
}
*
* 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,
{
#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);
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))
* 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] == '.'))
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++;
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';
* 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)
#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
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);
}
* 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,
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;
}
/**
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;
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)
char home_dir[PATH_MAX_LENGTH];
application_dir[0] = '\0';
- home_dir[0] = '\0';
/* application_dir could be zero-string. Safeguard against this.
*
{
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]);
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 */
- char* p;
+ /* 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 */
- char* p;
+ /* 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;
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];
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);
/* 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);
}
/**
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)
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
#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;
}
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;
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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
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
* 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);
/**
* 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
* 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.
*
* 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);
/**
/**
* 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:
*
* 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,
* 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:
*
* 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);
* 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);
/**
*
* 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);
*
* 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);
*
* 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);
* @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
* 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
*
* 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);
* 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);
* @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
*
* 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);
/**
*
* 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
*
* Gets the default slash separator.
*
- * Returns: default slash separator.
- */
+ * @return default slash separator.
+ **/
#ifdef _WIN32
#define PATH_DEFAULT_SLASH() "\\"
#define PATH_DEFAULT_SLASH_C() '\\'
*
* 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);
*
* 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);
*
* 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);
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
+};
--- /dev/null
+/* 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
+};
#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
#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"
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)
f = fopen(path, "rb");
if (f != NULL) {
log_cb(RETRO_LOG_INFO, "found MSU rom: %s\n", path);
- fclose(f);
+ fclose(f);
return path;
}
}
{ 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 },
};
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)
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)
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;
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;
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;
}
}
- 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
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;
}