RetroArch/file_path_special.c

657 lines
17 KiB
C
Raw Normal View History

2016-01-10 03:06:50 +00:00
/* RetroArch - A frontend for libretro.
2017-03-22 02:09:18 +00:00
* Copyright (C) 2011-2017 - Daniel De Matteis
2017-11-23 11:58:19 +00:00
*
2016-01-10 03:06:50 +00:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
2016-01-10 03:06:50 +00:00
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2016-01-10 03:06:50 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
/* Assume W-functions do not work below Win2K and Xbox platforms */
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
2015-09-04 18:51:50 +00:00
#ifdef _WIN32
#include <direct.h>
#else
2015-09-22 19:49:03 +00:00
#include <unistd.h>
#endif
2015-09-04 18:51:50 +00:00
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#endif
#ifdef __QNX__
#include <libgen.h>
#endif
2017-11-25 19:25:37 +00:00
#ifdef __HAIKU__
#include <kernel/image.h>
#endif
2016-09-08 03:48:43 +00:00
#include <stdlib.h>
#include <boolean.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <file/file_path.h>
2016-06-11 18:21:22 +00:00
#include <string/stdstring.h>
2015-11-23 11:03:38 +00:00
#include <compat/strl.h>
#include <compat/posix_string.h>
#include <retro_assert.h>
#include <retro_miscellaneous.h>
#include <encodings/utf.h>
2016-09-08 03:48:43 +00:00
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "configuration.h"
#include "file_path_special.h"
#include "paths.h"
2015-11-23 11:03:38 +00:00
#include "verbosity.h"
void fill_pathname_expand_special(char *out_path,
const char *in_path, size_t size)
{
#if !defined(RARCH_CONSOLE)
if (*in_path == '~')
{
const char *home = getenv("HOME");
if (home)
{
size_t src_size = strlcpy(out_path, home, size);
retro_assert(src_size < size);
out_path += src_size;
2016-06-27 05:47:15 +00:00
size -= src_size;
in_path++;
}
}
else if ((in_path[0] == ':') &&
2015-11-25 23:27:39 +00:00
(
(in_path[1] == '/')
2015-11-25 23:27:39 +00:00
#ifdef _WIN32
|| (in_path[1] == '\\')
#endif
2015-11-25 23:27:39 +00:00
)
)
{
2015-03-17 05:28:02 +00:00
size_t src_size;
char *application_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
2016-10-21 03:57:40 +00:00
application_dir[0] = '\0';
2015-03-17 05:28:02 +00:00
fill_pathname_application_path(application_dir,
PATH_MAX_LENGTH * sizeof(char));
path_basedir_wrapper(application_dir);
2015-03-17 05:28:02 +00:00
src_size = strlcpy(out_path, application_dir, size);
retro_assert(src_size < size);
free(application_dir);
out_path += src_size;
2015-03-17 05:28:02 +00:00
size -= src_size;
in_path += 2;
}
#endif
retro_assert(strlcpy(out_path, in_path, size) < size);
}
void fill_pathname_abbreviate_special(char *out_path,
const char *in_path, size_t size)
{
#if !defined(RARCH_CONSOLE)
unsigned i;
2015-06-29 19:39:00 +00:00
const char *candidates[3];
const char *notations[3];
char *application_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
const char *home = getenv("HOME");
2015-03-17 05:28:02 +00:00
2016-10-21 03:57:40 +00:00
application_dir[0] = '\0';
/* application_dir could be zero-string. Safeguard against this.
*
* Keep application dir in front of home, moving app dir to a
* new location inside home would break otherwise. */
2017-11-23 11:58:19 +00:00
/* ugly hack - use application_dir pointer
* before filling it in. C89 reasons */
2015-06-29 19:39:00 +00:00
candidates[0] = application_dir;
candidates[1] = home;
candidates[2] = NULL;
notations [0] = ":";
notations [1] = "~";
notations [2] = NULL;
2015-06-26 15:46:13 +00:00
2017-11-23 11:58:19 +00:00
fill_pathname_application_path(application_dir,
PATH_MAX_LENGTH * sizeof(char));
path_basedir_wrapper(application_dir);
2017-11-23 11:58:19 +00:00
for (i = 0; candidates[i]; i++)
{
2017-11-23 11:58:19 +00:00
if (!string_is_empty(candidates[i]) &&
strstr(in_path, candidates[i]) == in_path)
{
size_t src_size = strlcpy(out_path, notations[i], size);
retro_assert(src_size < size);
2017-11-23 11:58:19 +00:00
out_path += src_size;
size -= src_size;
in_path += strlen(candidates[i]);
2017-11-23 11:58:19 +00:00
if (!path_char_is_slash(*in_path))
{
retro_assert(strlcpy(out_path,
path_default_slash(), size) < size);
out_path++;
size--;
}
break; /* Don't allow more abbrevs to take place. */
}
}
free(application_dir);
#endif
retro_assert(strlcpy(out_path, in_path, size) < size);
}
2016-05-01 13:07:45 +00:00
bool fill_pathname_application_data(char *s, size_t len)
{
#if defined(_WIN32) && !defined(_XBOX)
#ifdef LEGACY_WIN32
2016-05-01 13:07:45 +00:00
const char *appdata = getenv("APPDATA");
if (appdata)
{
strlcpy(s, appdata, len);
return true;
}
#else
const wchar_t *appdataW = _wgetenv(L"APPDATA");
if (appdataW)
{
char *appdata = utf16_to_utf8_string_alloc(appdataW);
if (appdata)
{
strlcpy(s, appdata, len);
free(appdata);
return true;
}
}
#endif
2016-05-01 13:07:45 +00:00
#elif defined(OSX)
const char *appdata = getenv("HOME");
if (appdata)
{
fill_pathname_join(s, appdata,
2016-05-02 11:23:47 +00:00
"Library/Application Support/RetroArch", len);
2016-05-01 13:07:45 +00:00
return true;
}
2016-05-01 13:59:55 +00:00
#elif !defined(RARCH_CONSOLE)
2016-05-01 13:07:45 +00:00
const char *xdg = getenv("XDG_CONFIG_HOME");
const char *appdata = getenv("HOME");
/* XDG_CONFIG_HOME falls back to $HOME/.config. */
if (xdg)
{
fill_pathname_join(s, xdg, "retroarch", len);
return true;
}
if (appdata)
{
#ifdef __HAIKU__
fill_pathname_join(s, appdata,
2016-05-02 00:48:37 +00:00
"config/settings/retroarch/", len);
2016-05-01 13:07:45 +00:00
#else
fill_pathname_join(s, appdata,
2016-05-02 00:48:37 +00:00
".config/retroarch/", len);
2016-05-01 13:07:45 +00:00
#endif
return true;
}
#endif
return false;
}
2016-05-01 13:59:55 +00:00
#if !defined(RARCH_CONSOLE)
void fill_pathname_application_path(char *s, size_t len)
{
2016-10-30 02:33:35 +00:00
size_t i;
2015-10-12 19:02:07 +00:00
#ifdef __APPLE__
CFBundleRef bundle = CFBundleGetMainBundle();
#endif
2016-10-30 02:33:35 +00:00
#ifdef _WIN32
2016-10-30 02:34:11 +00:00
DWORD ret;
wchar_t wstr[PATH_MAX_LENGTH] = {0};
#endif
2016-10-30 02:33:35 +00:00
#ifdef __HAIKU__
image_info info;
int32_t cookie = 0;
#endif
(void)i;
2015-03-17 05:28:02 +00:00
if (!len)
return;
#ifdef _WIN32
#ifdef LEGACY_WIN32
ret = GetModuleFileNameA(GetModuleHandle(NULL), s, len);
#else
ret = GetModuleFileNameW(GetModuleHandle(NULL), wstr, ARRAY_SIZE(wstr));
if (*wstr)
{
2017-12-31 04:34:59 +00:00
char *str = utf16_to_utf8_string_alloc(wstr);
if (str)
{
strlcpy(s, str, len);
free(str);
}
}
#endif
s[ret] = '\0';
#elif defined(__APPLE__)
if (bundle)
{
CFURLRef bundle_url = CFBundleCopyBundleURL(bundle);
CFStringRef bundle_path = CFURLCopyPath(bundle_url);
CFStringGetCString(bundle_path, s, len, kCFStringEncodingUTF8);
CFRelease(bundle_path);
CFRelease(bundle_url);
2017-11-23 11:58:19 +00:00
retro_assert(strlcat(s, "nobin", len) < len);
return;
}
#elif defined(__HAIKU__)
while (get_next_image_info(0, &cookie, &info) == B_OK)
{
if (info.type == B_APP_IMAGE)
{
strlcpy(s, info.name, len);
return;
}
}
#elif defined(__QNX__)
char *buff = malloc(len);
if(_cmdname(buff))
2017-01-09 21:48:13 +00:00
strlcpy(s, buff, len);
free(buff);
#else
{
2015-06-29 19:39:00 +00:00
pid_t pid;
2015-06-26 17:37:03 +00:00
static const char *exts[] = { "exe", "file", "path/a.out" };
2016-10-27 08:16:26 +00:00
char link_path[255];
2015-06-26 17:37:03 +00:00
2016-10-21 03:57:40 +00:00
link_path[0] = *s = '\0';
2017-11-23 11:58:19 +00:00
pid = getpid();
2015-06-26 17:37:03 +00:00
2015-06-26 15:46:13 +00:00
/* Linux, BSD and Solaris paths. Not standardized. */
for (i = 0; i < ARRAY_SIZE(exts); i++)
{
2015-06-26 15:46:13 +00:00
ssize_t ret;
2015-06-26 17:37:03 +00:00
2015-06-26 15:46:13 +00:00
snprintf(link_path, sizeof(link_path), "/proc/%u/%s",
(unsigned)pid, exts[i]);
ret = readlink(link_path, s, len - 1);
2015-09-22 19:49:03 +00:00
2015-06-26 15:46:13 +00:00
if (ret >= 0)
{
s[ret] = '\0';
2015-06-26 15:46:13 +00:00
return;
}
}
}
2017-11-23 11:58:19 +00:00
2016-08-03 00:56:38 +00:00
RARCH_ERR("Cannot resolve application path! This should not happen.\n");
#endif
}
#endif
#ifdef HAVE_XMB
2017-01-14 09:06:56 +00:00
const char* xmb_theme_ident(void);
#endif
void fill_pathname_application_special(char *s,
size_t len, enum application_special_type type)
{
switch (type)
{
2016-06-11 19:51:28 +00:00
case APPLICATION_SPECIAL_DIRECTORY_AUTOCONFIG:
{
settings_t *settings = config_get_ptr();
fill_pathname_join(s,
2017-04-28 22:39:29 +00:00
settings->paths.directory_autoconfig,
2017-04-28 20:59:13 +00:00
settings->arrays.input_joypad_driver,
2016-06-11 19:51:28 +00:00
len);
}
break;
case APPLICATION_SPECIAL_DIRECTORY_CONFIG:
{
settings_t *settings = config_get_ptr();
/* Try config directory setting first,
* fallback to the location of the current configuration file. */
2017-04-28 22:39:29 +00:00
if (!string_is_empty(settings->paths.directory_menu_config))
strlcpy(s, settings->paths.directory_menu_config, len);
2016-09-30 02:43:16 +00:00
else if (!path_is_empty(RARCH_PATH_CONFIG))
2016-09-29 06:31:41 +00:00
fill_pathname_basedir(s, path_get(RARCH_PATH_CONFIG), len);
}
break;
2016-06-11 18:34:49 +00:00
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_ZARCH_ICONS:
#ifdef HAVE_ZARCH
{
}
#endif
break;
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_ZARCH_FONT:
#ifdef HAVE_ZARCH
{
char *s1 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
s1[0] = '\0';
2016-10-21 03:57:40 +00:00
2017-11-23 11:58:19 +00:00
fill_pathname_application_special(s1,
PATH_MAX_LENGTH * sizeof(char),
2016-06-11 18:34:49 +00:00
APPLICATION_SPECIAL_DIRECTORY_ASSETS_ZARCH);
fill_pathname_join(s,
s1, "Roboto-Condensed.ttf", len);
free(s1);
2016-06-11 18:34:49 +00:00
}
#endif
break;
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_ZARCH:
#ifdef HAVE_ZARCH
{
settings_t *settings = config_get_ptr();
2017-11-23 11:58:19 +00:00
fill_pathname_join(s,
2017-04-28 22:39:29 +00:00
settings->paths.directory_assets,
2016-06-11 18:34:49 +00:00
"zarch",
len);
}
#endif
break;
2016-06-11 17:55:27 +00:00
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_ICONS:
#ifdef HAVE_XMB
{
char *s1 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
char *s2 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
2016-10-21 03:57:40 +00:00
s1[0] = s2[0] = '\0';
fill_pathname_application_special(s1,
PATH_MAX_LENGTH * sizeof(char),
2016-06-11 17:55:27 +00:00
APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB);
fill_pathname_join(s2, s1, "png",
PATH_MAX_LENGTH * sizeof(char)
);
fill_pathname_slash(s2,
PATH_MAX_LENGTH * sizeof(char)
);
strlcpy(s, s2, len);
free(s1);
free(s2);
}
2016-06-11 18:11:36 +00:00
#endif
break;
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_BG:
#ifdef HAVE_XMB
{
settings_t *settings = config_get_ptr();
2017-04-28 22:39:29 +00:00
if (!string_is_empty(settings->paths.path_menu_wallpaper))
strlcpy(s, settings->paths.path_menu_wallpaper, len);
2016-06-11 18:11:36 +00:00
else
2016-06-11 18:16:34 +00:00
{
char *s1 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
2016-10-21 03:57:40 +00:00
s1[0] = '\0';
fill_pathname_application_special(s1,
PATH_MAX_LENGTH * sizeof(char),
2016-06-11 18:16:34 +00:00
APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_ICONS);
2016-06-27 05:47:15 +00:00
fill_pathname_join(s, s1,
file_path_str(FILE_PATH_BACKGROUND_IMAGE),
len);
free(s1);
2016-06-11 18:16:34 +00:00
}
2016-06-11 18:11:36 +00:00
}
#endif
break;
2016-06-11 17:55:27 +00:00
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB:
#ifdef HAVE_XMB
{
char *s1 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
char *s2 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
settings_t *settings = config_get_ptr();
2016-10-21 03:57:40 +00:00
s1[0] = s2[0] = '\0';
fill_pathname_join(
s1,
2017-04-28 22:39:29 +00:00
settings->paths.directory_assets,
"xmb",
PATH_MAX_LENGTH * sizeof(char)
);
fill_pathname_join(s2,
s1, xmb_theme_ident(),
PATH_MAX_LENGTH * sizeof(char)
);
strlcpy(s, s2, len);
free(s1);
free(s2);
}
#endif
break;
2016-06-11 17:55:27 +00:00
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI:
#ifdef HAVE_MATERIALUI
{
2016-06-11 17:46:37 +00:00
settings_t *settings = config_get_ptr();
fill_pathname_join(
2016-06-11 17:46:37 +00:00
s,
2017-04-28 22:39:29 +00:00
settings->paths.directory_assets,
"glui",
2016-06-11 17:46:37 +00:00
len);
}
#endif
break;
2016-06-11 17:55:27 +00:00
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI_ICONS:
2016-06-11 17:46:37 +00:00
#ifdef HAVE_MATERIALUI
{
char *s1 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
2016-10-21 03:57:40 +00:00
s1[0] = '\0';
2016-06-11 17:55:27 +00:00
fill_pathname_application_special(s1,
PATH_MAX_LENGTH * sizeof(char),
APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI);
fill_pathname_slash(s1,
PATH_MAX_LENGTH * sizeof(char)
);
strlcpy(s, s1, len);
free(s1);
}
#endif
break;
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI_FONT:
#ifdef HAVE_MATERIALUI
{
char *s1 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
2016-10-21 03:57:40 +00:00
s1[0] = '\0';
fill_pathname_application_special(s1,
PATH_MAX_LENGTH * sizeof(char),
APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI);
fill_pathname_join(s, s1, "font.ttf", len);
free(s1);
}
2016-06-11 18:01:33 +00:00
#endif
break;
case APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_FONT:
#ifdef HAVE_XMB
{
2016-06-11 18:21:22 +00:00
settings_t *settings = config_get_ptr();
2016-06-11 18:01:33 +00:00
2017-04-28 22:39:29 +00:00
if (!string_is_empty(settings->paths.path_menu_xmb_font))
strlcpy(s, settings->paths.path_menu_xmb_font, len);
2016-06-11 18:21:22 +00:00
else
2016-06-11 18:22:03 +00:00
{
char *s1 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
2016-10-21 03:57:40 +00:00
s1[0] = '\0';
fill_pathname_application_special(s1,
PATH_MAX_LENGTH * sizeof(char),
2016-06-11 18:22:03 +00:00
APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB);
2016-06-27 05:47:15 +00:00
fill_pathname_join(s, s1,
file_path_str(FILE_PATH_TTF_FONT),
len);
free(s1);
2016-06-11 18:22:03 +00:00
}
2016-06-11 18:01:33 +00:00
}
#endif
break;
2017-11-23 11:58:19 +00:00
case APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES:
{
char *s1 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
char *s2 = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
settings_t *settings = config_get_ptr();
s1[0] = s2[0] = '\0';
fill_pathname_join(s1,
settings->paths.directory_thumbnails,
"cheevos",
len);
fill_pathname_join(s2,
s1, "badges",
PATH_MAX_LENGTH * sizeof(char)
);
fill_pathname_slash(s2,
PATH_MAX_LENGTH * sizeof(char)
);
strlcpy(s, s2, len);
free(s1);
free(s2);
}
break;
2016-06-11 17:55:27 +00:00
case APPLICATION_SPECIAL_NONE:
default:
break;
}
}
/**
* 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
*/
void fill_short_pathname_representation_wrapper(char* out_rep,
const char *in_path, size_t size)
{
char *path_short = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
#ifdef HAVE_COMPRESSION
char *last_slash = NULL;
#endif
path_short[0] = '\0';
fill_pathname(path_short, path_basename(in_path), "",
PATH_MAX_LENGTH * sizeof(char)
);
#ifdef HAVE_COMPRESSION
last_slash = find_last_slash(path_short);
if (last_slash != NULL)
{
/* We handle paths like:
* /path/to/file.7z#mygame.img
* short_name: mygame.img:
*
* We check whether something is actually
* after the hash to avoid going over the buffer.
*/
retro_assert(strlen(last_slash) > 1);
strlcpy(out_rep, last_slash + 1, size);
free(path_short);
return;
}
#endif
fill_short_pathname_representation(out_rep, in_path, size);
free(path_short);
}
/**
* path_basedir:
* @path : path
*
* Extracts base directory by mutating path.
* Keeps trailing '/'.
**/
void path_basedir_wrapper(char *path)
{
char *last = NULL;
if (strlen(path) < 2)
return;
#ifdef HAVE_COMPRESSION
/* We want to find the directory with the archive in basedir. */
last = (char*)path_get_archive_delim(path);
if (last)
*last = '\0';
#endif
last = find_last_slash(path);
if (last)
last[1] = '\0';
else
snprintf(path, 3, ".%s", path_default_slash());
}