Merge pull request #320 from libretro/createconfig

Create default config
This commit is contained in:
Squarepusher 2013-10-01 07:46:53 -07:00
commit 1d60ea18c2
7 changed files with 164 additions and 30 deletions

View File

@ -16,8 +16,21 @@ retroarch-joyconfig \- Tool to configure joypad bindings for \fBretroarch\fR.
It reads in necessary joypad bindings for a certain player and joypad. It reads in necessary joypad bindings for a certain player and joypad.
.SH "EXAMPLE COMMANDLINES" .SH "EXAMPLE COMMANDLINES"
.TP
\fBConfigure joypad for player 1, using first joypad. Configuration is dumped to a file.\fR
retroarch-joyconfig -p 1 -j 0 -o inputconfig.cfg
.TP
\fBConfigure joypad for player 1, using first joypad. Update retroarch.cfg directly.\fR
retroarch-joyconfig -p 1 -j 0 -i retroarch.cfg -o retroarch.cfg
.TP
\fBConfigure joypad for player 1, using first joypad. Configuration is dumped to stdout.\fR
retroarch-joyconfig -p 1 -j 0 retroarch-joyconfig -p 1 -j 0
.TP
\fBCreate an autoconfig file using first joypad.\fR
retroarch-joyconfig -j 0 -a ~/.config/retroarch/autoconfig/pad.cfg retroarch-joyconfig -j 0 -a ~/.config/retroarch/autoconfig/pad.cfg
.SH "GENERAL OPTIONS" .SH "GENERAL OPTIONS"

View File

@ -4,7 +4,7 @@
.SH NAME .SH NAME
retroarch \- A simple frontend for the libretro API. retroarch \- The reference frontend for the libretro API.
.SH SYNOPSIS .SH SYNOPSIS
@ -12,13 +12,32 @@ retroarch \- A simple frontend for the libretro API.
.SH "DESCRIPTION" .SH "DESCRIPTION"
\fBretroarch\fR is an emulator frontend for the libretro API. \fBretroarch\fR is the reference frontend for the libretro API.
libretro provides emulation of a game system, and can be implemented by any frontend. libretro is an abstraction of a game system, and can be implemented by any frontend.
The libretro API is designed for games and emulators.
\fBretroarch\fR focuses on exposing needed functionality for the game system through the use of command line and configuration files. \fBretroarch\fR focuses on exposing needed functionality for the game system through the use of command line and configuration files.
It also features a simple built-in UI called RGUI.
.SH "EXAMPLE COMMANDLINE" .SH "EXAMPLE COMMANDLINE"
.TP
\fBLoad a ROM, using a specific libretro core and config file.\fR
retroarch --config ~/.config/retroarch/retroarch.cfg --libretro /path/to/libretro/core.so /path/to/rom.rom --verbose retroarch --config ~/.config/retroarch/retroarch.cfg --libretro /path/to/libretro/core.so /path/to/rom.rom --verbose
.TP
\fBNo command line options will start RetroArch in RGUI mode.\fR
retroarch
.TP
\fBStart RetroArch in RGUI, with verbose logging.\fR
retroarch --menu --verbose
.SH "RGUI"
RGUI is the built-in GUI system for RetroArch. It is aimed at being controlled with a gamepad only.
.SH "DEFAULT CONTROLS"
By default, only keyboard input is accepted.
.SH "GENERAL OPTIONS" .SH "GENERAL OPTIONS"
.TP .TP
@ -74,14 +93,20 @@ Always starts RetroArch in fullscreen. Disregards settings in configuration file
Sets the configuration file path. \fBretroarch\fR will use this path to load the configuration file. Sets the configuration file path. \fBretroarch\fR will use this path to load the configuration file.
Should this not be defined, \fBretroarch\fR will look in platform specific paths to attempt finding the config file. Should this not be defined, \fBretroarch\fR will look in platform specific paths to attempt finding the config file.
/etc/retroarch.cfg (when installed), or retroarch.cfg in the source tarball serves as a skeleton configuration file. /etc/retroarch.cfg (when installed), or retroarch.cfg in the source tarball serves as a skeleton configuration file.
If PATH is a directory, RetroArch will treat this as the config file directory, where the state file name will be inferred from the rom name (*.cfg). /etc/retroarch.cfg should serve as a skeleton config only, and not used as a config file.
If a config cannot be found when using directory path, the default config path will be used instead.
.IP .IP
Unix-like systems will look in $XDG_CONFIG_HOME/retroarch/retroarch.cfg first. If $XDG_CONFIG_HOME is not defined, it is assumed to be $HOME/.config as per specification. Then it will try $HOME/.retroarch.cfg. Last, it will try /etc/retroarch.cfg. If no configuration is found, default settings will be assumed. A configuration file does not need to define every possible option, only those that should be overridden. Unix-like systems will look in $XDG_CONFIG_HOME/retroarch/retroarch.cfg first. If $XDG_CONFIG_HOME is not defined, it is assumed to be $HOME/.config as per specification. Then it will try $HOME/.retroarch.cfg. If both paths fail, RetroArch will try to create a new, default config file in $XDG_CONFIG_HOME/retroarch/retroarch.cfg (or the $HOME/.config default for $XDG_CONFIG_HOME). If no configuration is found at all, default settings will be assumed.
RetroArch will not attempt to load the skeleton config /etc/retroarch.cfg.
A configuration file does not need to define every possible option, only those which should be overridden.
If config_save_on_exit = true is set in the config file, RetroArch will overwrite the config file on exit. Settings can be changed from within RGUI.
If RetroArch overwrites a config file, formatting, comments, etc will be lost.
If RetroArch creates a default config file, it will have config_save_on_exit set automatically.
.IP .IP
Windows will look in retroarch.cfg in same folder where retroarch.exe resides. Windows will look in retroarch.cfg in same folder where retroarch.exe resides.
A default config file will also be created in the same manner as Unix.
.TP .TP
\fB--appendconfig PATH\fR \fB--appendconfig PATH\fR

2
file.h
View File

@ -74,6 +74,8 @@ bool path_is_directory(const char *path);
bool path_file_exists(const char *path); bool path_file_exists(const char *path);
const char *path_get_extension(const char *path); const char *path_get_extension(const char *path);
bool path_mkdir(const char *dir);
// Removes all text after and including the last '.' // Removes all text after and including the last '.'
char *path_remove_extension(char *path); char *path_remove_extension(char *path);

View File

@ -19,6 +19,7 @@
#include "boolean.h" #include "boolean.h"
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <errno.h>
#include "compat/strl.h" #include "compat/strl.h"
#include "compat/posix_string.h" #include "compat/posix_string.h"
@ -44,6 +45,7 @@
#else #else
#include <io.h> #include <io.h>
#include <fcntl.h> #include <fcntl.h>
#include <direct.h>
#include <windows.h> #include <windows.h>
#endif #endif
#else #else
@ -564,6 +566,64 @@ void path_resolve_realpath(char *buf, size_t size)
#endif #endif
} }
static bool path_mkdir_norecurse(const char *dir)
{
#if (defined(_WIN32) && !defined(_XBOX)) || !defined(RARCH_CONSOLE)
#ifdef _WIN32
int ret = _mkdir(dir);
#else
int ret = mkdir(dir, 0750);
#endif
if (ret < 0 && errno == EEXIST && path_is_directory(dir)) // Don't treat this as an error.
ret = 0;
if (ret < 0)
RARCH_ERR("mkdir(%s) error: %s.\n", dir, strerror(errno));
return ret == 0;
#else
(void)dir;
return false;
#endif
}
bool path_mkdir(const char *dir)
{
const char *target = NULL;
char *basedir = strdup(dir); // Use heap. Real chance of stack overflow if we recurse too hard.
bool ret = true;
if (!basedir)
return false;
path_parent_dir(basedir);
if (!*basedir || !strcmp(basedir, dir))
{
ret = false;
goto end;
}
if (path_is_directory(basedir))
{
target = dir;
ret = path_mkdir_norecurse(dir);
}
else
{
target = basedir;
ret = path_mkdir(basedir);
if (ret)
{
target = dir;
ret = path_mkdir_norecurse(dir);
}
}
end:
if (target && !ret)
RARCH_ERR("Failed to create directory: \"%s\".\n", target);
free(basedir);
return ret;
}
void fill_pathname_resolve_relative(char *out_path, const char *in_refpath, const char *in_path, size_t size) void fill_pathname_resolve_relative(char *out_path, const char *in_refpath, const char *in_path, size_t size)
{ {
if (path_is_absolute(in_path)) if (path_is_absolute(in_path))

View File

@ -67,6 +67,7 @@ static bool libretro_install_core(const char *path_prefix,
void rarch_make_dir(const char *x, const char *name) void rarch_make_dir(const char *x, const char *name)
{ {
// FIXME: This should use path_mkdir() in file_path.c.
RARCH_LOG("Checking directory name %s [%s]\n", name, x); RARCH_LOG("Checking directory name %s [%s]\n", name, x);
if (strlen(x) > 0) if (strlen(x) > 0)
{ {

View File

@ -600,9 +600,9 @@ static int16_t input_state(unsigned port, unsigned device, unsigned index, unsig
} }
#ifdef _WIN32 #ifdef _WIN32
#define RARCH_DEFAULT_CONF_PATH_STR "\n\t\tDefaults to retroarch.cfg in same directory as retroarch.exe." #define RARCH_DEFAULT_CONF_PATH_STR "\n\t\tDefaults to retroarch.cfg in same directory as retroarch.exe.\n\t\tIf a default config is not found, RetroArch will attempt to create one."
#else #else
#define RARCH_DEFAULT_CONF_PATH_STR "\n\t\tBy default looks for config in $XDG_CONFIG_HOME/retroarch/retroarch.cfg,\n\t\t$HOME/.config/retroarch/retroarch.cfg,\n\t\tand $HOME/.retroarch.cfg." #define RARCH_DEFAULT_CONF_PATH_STR "\n\t\tBy default looks for config in $XDG_CONFIG_HOME/retroarch/retroarch.cfg,\n\t\t$HOME/.config/retroarch/retroarch.cfg,\n\t\tand $HOME/.retroarch.cfg.\n\t\tIf a default config is not found, RetroArch will attempt to create one."
#endif #endif
#include "config.features.h" #include "config.features.h"
@ -772,17 +772,6 @@ static void set_paths(const char *path)
// do not overwrite it as this was initialized before in a menu or otherwise. // do not overwrite it as this was initialized before in a menu or otherwise.
if (!*g_settings.system_directory) if (!*g_settings.system_directory)
fill_pathname_basedir(g_settings.system_directory, path, sizeof(g_settings.system_directory)); fill_pathname_basedir(g_settings.system_directory, path, sizeof(g_settings.system_directory));
if (*g_extern.config_path && path_is_directory(g_extern.config_path))
{
fill_pathname_dir(g_extern.config_path, g_extern.basename, ".cfg", sizeof(g_extern.config_path));
RARCH_LOG("Redirecting config file to \"%s\".\n", g_extern.config_path);
if (!path_file_exists(g_extern.config_path))
{
*g_extern.config_path = '\0';
RARCH_LOG("Did not find config file. Using system default.\n");
}
}
} }
static void parse_input(int argc, char *argv[]) static void parse_input(int argc, char *argv[])

View File

@ -392,6 +392,28 @@ static config_file_t *open_default_config_file(void)
} }
} }
// Try to create a new config file.
if (!conf)
{
conf = config_file_new(NULL);
bool saved = false;
if (conf) // Since this is a clean config file, we can safely use config_save_on_exit.
{
fill_pathname_resolve_relative(conf_path, app_path, "retroarch.cfg", sizeof(conf_path));
config_set_bool(conf, "config_save_on_exit", true);
saved = config_file_write(conf, conf_path);
}
if (saved)
RARCH_WARN("Created new config file in: \"%s\".\n", conf_path); // WARN here to make sure user has a good chance of seeing it.
else
{
RARCH_ERR("Failed to create new config file in: \"%s\".\n", conf_path);
config_file_free(conf);
conf = NULL;
}
}
if (conf) if (conf)
strlcpy(g_extern.config_path, conf_path, sizeof(g_extern.config_path)); strlcpy(g_extern.config_path, conf_path, sizeof(g_extern.config_path));
#elif !defined(__CELLOS_LV2__) && !defined(_XBOX) #elif !defined(__CELLOS_LV2__) && !defined(_XBOX)
@ -401,9 +423,9 @@ static config_file_t *open_default_config_file(void)
// XDG_CONFIG_HOME falls back to $HOME/.config. // XDG_CONFIG_HOME falls back to $HOME/.config.
if (xdg) if (xdg)
snprintf(conf_path, sizeof(conf_path), "%s/retroarch/retroarch.cfg", xdg); fill_pathname_join(conf_path, xdg, "retroarch/retroarch.cfg", sizeof(conf_path));
else if (home) else if (home)
snprintf(conf_path, sizeof(conf_path), "%s/.config/retroarch/retroarch.cfg", home); fill_pathname_join(conf_path, home, ".config/retroarch/retroarch.cfg", sizeof(conf_path));
if (xdg || home) if (xdg || home)
{ {
@ -414,20 +436,42 @@ static config_file_t *open_default_config_file(void)
// Fallback to $HOME/.retroarch.cfg. // Fallback to $HOME/.retroarch.cfg.
if (!conf && home) if (!conf && home)
{ {
snprintf(conf_path, sizeof(conf_path), "%s/.retroarch.cfg", home); fill_pathname_join(conf_path, home, ".retroarch.cfg", sizeof(conf_path));
RARCH_LOG("Looking for config in: \"%s\".\n", conf_path); RARCH_LOG("Looking for config in: \"%s\".\n", conf_path);
conf = config_file_new(conf_path); conf = config_file_new(conf_path);
} }
// Try this as a last chance ... // Try to create a new config file.
if (!conf) if (!conf && (home || xdg))
{ {
#ifndef GLOBAL_CONFIG_DIR // XDG_CONFIG_HOME falls back to $HOME/.config.
#define GLOBAL_CONFIG_DIR "/etc" if (xdg)
#endif fill_pathname_join(conf_path, xdg, "retroarch/retroarch.cfg", sizeof(conf_path));
fill_pathname_join(conf_path, GLOBAL_CONFIG_DIR, "retroarch.cfg", sizeof(conf_path)); else if (home)
RARCH_LOG("Looking for config in: \"%s\".\n", conf_path); fill_pathname_join(conf_path, home, ".config/retroarch/retroarch.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
char basedir[PATH_MAX];
fill_pathname_basedir(basedir, conf_path, sizeof(basedir));
if (path_mkdir(basedir))
{
conf = config_file_new(NULL);
bool saved = false;
if (conf)
{
config_set_bool(conf, "config_save_on_exit", true); // Since this is a clean config file, we can safely use config_save_on_exit.
saved = config_file_write(conf, conf_path);
}
if (saved)
RARCH_WARN("Created new config file in: \"%s\".\n", conf_path); // WARN here to make sure user has a good chance of seeing it.
else
{
RARCH_ERR("Failed to create new config file in: \"%s\".\n", conf_path);
config_file_free(conf);
conf = NULL;
}
}
} }
if (conf) if (conf)