#ifdef HAVE_CONFIG_H #include "config.h" #endif #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include "fs-uae.h" #include "config-common.h" #include "paths.h" #include "options.h" struct multipath g_paths[5] = {}; static char *g_fs_uae_state_base_name; static const char* fs_uae_home_dir() { static const char *path; if (!path) { path = fs_get_home_dir(); if (path == NULL) { fs_log("WARNING: did not find home directory\n"); path = g_strdup(""); } fs_log("- using home directory \"%s\"\n", path); } return path; } const char* fs_uae_exe_dir() { static const char *path; if (!path) { char *app_dir = (char*) malloc(MAX_PATH); fs_get_application_exe_dir(app_dir, MAX_PATH); char *p = g_strdup(app_dir); g_free(app_dir); path = p; fs_log("- using $exe directory \"%s\"\n", path); } return path; } static const char* fs_uae_app_dir() { static const char *path; if (!path) { char *app_dir = (char*) malloc(MAX_PATH); fs_get_application_exe_dir(app_dir, MAX_PATH); char *p = g_strdup(app_dir); g_free(app_dir); path = p; #ifdef MACOSX char *base_name = g_path_get_basename(p); if (strcmp(base_name, "MacOS") == 0) { char *temp; temp = p; p = g_path_get_dirname(p); g_free(temp); temp = p; p = g_path_get_dirname(p); g_free(temp); temp = p; p = g_path_get_dirname(p); g_free(temp); path = p; } free(base_name); #endif fs_log("- using $app directory \"%s\"\n", path); } return path; } static const char* fs_uae_documents_dir() { static const char *path; if (!path) { path = fs_get_documents_dir(); if (path == NULL) { fs_log("WARNING: did not find documents directory\n"); path = fs_uae_home_dir(); } int result = g_mkdir_with_parents(path, 0755); if (result == -1) { fs_emu_warning("Documents directory does not " "exist: %s", path); path = fs_uae_home_dir(); } fs_log("- using documents directory \"%s\"\n", path); } return path; } static char* read_custom_path(const char *key) { char *key_path = g_build_filename(fse_user_config_dir(), "fs-uae", key, NULL); fs_log("- checking %s\n", key_path); if (fs_path_is_file(key_path)) { FILE * f = fopen(key_path, "rb"); free(key_path); if (f == NULL) { fs_log("- file exists but could not open\n"); return NULL; } char *buffer = (char *) malloc(PATH_MAX + 1); int read_bytes = fread(buffer, 1, PATH_MAX, f); int eof = feof(f); fclose(f); if (!eof) { fs_log("- did not get EOF\n"); free(buffer); return NULL; } buffer[read_bytes] = '\0'; g_strchomp(buffer); fs_log("- read from file: %s\n", buffer); char *result = fs_uae_expand_path(buffer); free(buffer); fs_log("- expanded path: %s\n", result); return result; } return NULL; } const char* fs_uae_base_dir(void) { static const char* path; if (path) { return path; } path = fs_config_get_const_string("base_dir"); if (path) { fs_log("base specified via base_dir option\n"); path = fs_uae_expand_path(path); } if (path == NULL) { // FIXME: deprecated const char *env_path = getenv("FS_UAE_BASE_DIR"); if (env_path && env_path[0]) { path = env_path; fs_log("base specified via FS_UAE_BASE_DIR\n"); fs_emu_deprecated("FS_UAE_BASE_DIR is deprecated"); } } if (path == NULL) { // check for portable dir char buffer[MAX_PATH]; fs_get_application_exe_dir(buffer, MAX_PATH); char *next = g_strdup(buffer); char *orig = g_strdup(":INVALID;"); while (strcmp(orig, next) != 0) { char *test = g_build_filename(next, "Portable.ini", NULL); fs_log("checking %s\n", test); if (fs_path_exists(test)) { path = next; fs_log("using portable base dir %s\n", path); g_free(orig); break; } g_free(test); g_free(orig); orig = next; next = g_path_get_dirname(next); } } if (path == NULL) { path = read_custom_path("base-dir"); } if (path == NULL) { fs_log("- using base dir $DOCUMENTS/FS-UAE\n"); path = g_build_filename(fs_uae_documents_dir(), "FS-UAE", NULL); } int result = g_mkdir_with_parents(path, 0755); if (result == -1) { fs_emu_warning("Could not create base directory " "at %s", path); path = fs_uae_documents_dir(); } fs_log("- using base ($BASE / $FSUAE) directory \"%s\"\n", path); return path; } static char *get_or_create_default_dir(const char *name, const char *key1, const char *key2, const char *dashed_key, int create, int cache) { char *path = NULL; if (path == NULL && key1 != NULL) { path = fs_config_get_string(key1); } if (path == NULL && key2 != NULL) { path = fs_config_get_string(key2); } if (path == NULL && dashed_key != NULL) { path = read_custom_path(dashed_key); } if (path == NULL) { if (cache) { path = g_build_filename(fs_uae_cache_dir(), name, NULL); } else { path = g_build_filename(fs_uae_base_dir(), name, NULL); } } char *expanded_path = fs_uae_expand_path_and_free(path); path = fs_uae_resolve_path(expanded_path, FS_UAE_DIR_PATHS); free(expanded_path); if (create) { int result = g_mkdir_with_parents(path, 0755); if (result == -1) { fs_emu_warning("Could not create %s directory", name); free(path); path = g_strdup(fs_uae_base_dir()); } } fs_log("- using \"%s\" directory \"%s\"\n", name, path); return path; } static char *create_default_cache_dir(const char *name, const char *key1, const char *key2, const char *dashed_key) { return get_or_create_default_dir(name, key1, key2, dashed_key, 1, 1); } static char *create_default_dir(const char *name, const char *key1, const char *key2, const char *dashed_key) { return get_or_create_default_dir(name, key1, key2, dashed_key, 1, 0); } static char *get_default_dir(const char *name, const char *key1, const char *key2, const char *dashed_key) { return get_or_create_default_dir(name, key1, key2, dashed_key, 0, 0); } const char *fs_uae_kickstarts_dir() { static const char *path; if (!path) { path = create_default_dir("Kickstarts", "kickstarts_dir", "roms_dir", "kickstarts-dir"); } return path; } const char *fs_uae_configurations_dir() { static const char *path; if (!path) { path = create_default_dir("Configurations", "configurations_dir", NULL, "configurations-dir"); } return path; } static const char *fs_uae_save_states_dir() { static const char *path; if (!path) { path = create_default_dir("Save States", "save_states_dir", NULL, "save-states-dir"); } return path; } static const char *fs_uae_state_dir_path() { char *free_state_dir_name = NULL; static const char *path; if (!path) { fs_log("fs_uae_state_dir:\n"); path = fs_config_get_const_string("state_dir"); if (path && path[0]) { fs_log("state_dir was explicitly set to: %s\n", path); char *expanded_path = fs_uae_expand_path(path); path = fs_uae_resolve_path(expanded_path, FS_UAE_DIR_PATHS); free(expanded_path); return path; } const char *base = fs_uae_save_states_dir(); fs_log("save_states_dir: %s\n", base); const char *state_dir_name = fs_config_get_const_string( "state_dir_name"); if (!state_dir_name || !state_dir_name[0]) { if (g_fs_uae_config_file_path) { char *n = g_path_get_basename(g_fs_uae_config_file_path); for (int i = strlen(n) - 1; i >= 0; i--) { if (n[i] == '.') { n[i] = '\0'; break; } } free_state_dir_name = g_strdup(n); state_dir_name = free_state_dir_name; g_free(n); } if (!state_dir_name || !state_dir_name[0]) { state_dir_name = "Default"; } fs_log("save_dir_name not set, using %s\n", state_dir_name); } path = g_build_filename(base, state_dir_name, NULL); } if (free_state_dir_name) { g_free(free_state_dir_name); } return path; } const char *fs_uae_state_dir() { static const char *path; if (!path) { path = fs_uae_state_dir_path(); if (!path || !path[0]) { path = fs_uae_base_dir(); fs_log("reverting state dir to: %s\n", path); } fs_log("- using state dir %s\n", path); int result = g_mkdir_with_parents(path, 0755); if (result == -1) { fs_emu_warning("Could not create state directory"); path = fs_uae_base_dir(); } fs_log("final state dir path: %s\n", path); } return path; } const char *fs_uae_cdroms_dir() { static const char *path; if (!path) { path = create_default_dir("CD-ROMs", "cdroms_dir", NULL, "cdroms-dir"); } return path; } const char *fs_uae_hard_drives_dir() { static const char *path; if (!path) { path = create_default_dir("Hard Drives", "hard_drives_dir", NULL, "hard-drives-dir"); } return path; } const char *fs_uae_floppies_dir() { static const char *path; if (!path) { path = create_default_dir("Floppies", "floppies_dir", NULL, "floppies-dir"); } return path; } const char *fs_uae_controllers_dir() { static const char *path; if (!path) { path = create_default_dir("Controllers", "controllers_dir", NULL, "controllers-dir"); } return path; } const char *fs_uae_logs_dir() { static const char *path; if (!path) { path = create_default_cache_dir("Logs", "logs_dir", NULL, "logs-dir"); } return path; } const char *fs_uae_module_ripper_dir() { static const char *path; if (!path) { path = create_default_cache_dir("Modules", "module_ripper_dir", NULL, "module-ripper-dir"); } return path; } const char *fs_uae_cache_dir(void) { static const char *path; if (!path) { path = create_default_dir("Cache", "cache_dir", NULL, "cache-dir"); } return path; } const char *fs_uae_data_dir(void) { static const char *path; if (!path) { path = create_default_dir("Data", "data_dir", NULL, "data-dir"); } return path; } const char *fs_uae_kickstarts_cache_dir() { static const char *path; if (!path) { path = g_build_filename(fs_uae_cache_dir(), "Kickstarts", NULL); int result = g_mkdir_with_parents(path, 0755); if (result == -1) { fs_emu_warning("Could not create kickstarts cache directory"); path = fs_uae_base_dir(); } } return path; } const char *fs_uae_themes_dir() { static const char *path; if (!path) { path = create_default_dir("Themes", "themes_dir", NULL, "themes-dir"); } return path; } const char *fs_uae_plugins_dir(void) { static const char *path; if (!path) { path = get_default_dir("Plugins", "plugins_dir", NULL, "plugins-dir"); } return path; } const char *fs_uae_system_dir(void) { static const char *path; if (!path) { path = get_default_dir("System", "system_dir", NULL, "system-dir"); } return path; } static const char *fs_uae_temp_dir(void) { static const char *path = NULL; if (path == NULL) { path = g_dir_make_tmp("fs-uae-TEMP-XXXXXX", NULL); fs_log("FS-UAE $TEMP = %s\n", path); } return path; } static bool check_path_prefix(const char *path, const char *prefix, int *r) { if (g_str_has_prefix(path, prefix)) { *r = strlen(prefix); if (path[*r] == '/' || path[*r] == '\\') { *r += 1; return true; } else if (path[*r] == '\0') { return true; } } return false; } char *fs_uae_expand_path(const char* path) { char* lower = g_ascii_strdown(path, -1); int replace = 0; const char *replace_with = NULL; if (check_path_prefix(lower, "~", &replace)) { replace_with = fs_uae_home_dir(); } else if (check_path_prefix(lower, "$home", &replace)) { replace_with = fs_uae_home_dir(); } else if (check_path_prefix(lower, "$app", &replace)) { replace_with = fs_uae_app_dir(); } else if (check_path_prefix(lower, "$exe", &replace)) { replace_with = fs_uae_exe_dir(); } else if (check_path_prefix(lower, "$fsuae", &replace)) { replace_with = fs_uae_base_dir(); } else if (check_path_prefix(lower, "$base", &replace)) { replace_with = fs_uae_base_dir(); } else if (check_path_prefix(lower, "$documents", &replace)) { replace_with = fs_uae_documents_dir(); } else if (check_path_prefix(lower, "$config", &replace)) { replace_with = g_fs_uae_config_dir_path; } else if (check_path_prefix(lower, "$temp", &replace)) { replace_with = fs_uae_temp_dir(); } free(lower); if (replace_with) { const char *src = path + replace; return g_build_filename(replace_with, src, NULL); } else { return g_strdup(path); } } char *fs_uae_expand_path_and_free(char *path) { char *p = fs_uae_expand_path(path); free(path); return p; } const char *fs_uae_get_state_base_name() { return g_fs_uae_state_base_name; } void fs_uae_set_state_base_name(const char *base_name) { g_fs_uae_state_base_name = g_strdup(base_name); } static void fix_separators(char *path) { #ifdef WINDOWS char *p = path; while (*p) { if (*p == '\\') { *p = '/'; } p++; } #endif } char *fs_uae_resolve_path(const char *name, int type) { if (name[0] == '\0') { fs_log("resolve_path (empty string)\n"); return g_strdup(""); } else if (g_path_is_absolute(name)) { fs_log("resolve_path %s (absolute)\n", name); char *path = g_strdup(name); fix_separators(path); return path; } else { fs_log("resolve_path %s (relative)\n", name); for (int i = 0; i < MAX_PATHS; i++) { if (!g_paths[type].path[i] || g_paths[type].path[i][0] == '\0') { continue; } char *path = g_build_filename(g_paths[type].path[i], name, NULL); fs_log("checking %s\n", path); if (fs_path_exists(path)) { fs_log("- found %s\n", path); fix_separators(path); return path; } free(path); } } fs_log("WARNING: did not find path\n", name); char *path = g_strdup(name); fix_separators(path); return path; } char *fs_uae_resolve_path_and_free(char *name, int type) { char *result = fs_uae_resolve_path(name, type); free(name); return result; } void fs_uae_init_path_resolver(void) { bool relative_paths = true; if (fs_config_false(OPTION_RELATIVE_PATHS)) { relative_paths = false; } int k; k = 0; if (relative_paths) { /* Current working directory should always come first (index 0) */ g_paths[FS_UAE_DIR_PATHS].path[k++] = g_strdup("."); } if (g_fs_uae_config_dir_path) { g_paths[FS_UAE_DIR_PATHS].path[k++] = g_strdup( g_fs_uae_config_dir_path); } k = 0; if (relative_paths) { g_paths[FS_UAE_FLOPPY_PATHS].path[k++] = g_strdup("."); } if (g_fs_uae_config_dir_path) { g_paths[FS_UAE_FLOPPY_PATHS].path[k++] = g_strdup( g_fs_uae_config_dir_path); } if (fs_uae_floppies_dir()) { g_paths[FS_UAE_FLOPPY_PATHS].path[k++] = g_strdup( fs_uae_floppies_dir()); } k = 0; if (relative_paths) { g_paths[FS_UAE_CD_PATHS].path[k++] = g_strdup("."); } if (g_fs_uae_config_dir_path) { g_paths[FS_UAE_CD_PATHS].path[k++] = g_strdup( g_fs_uae_config_dir_path); } if (fs_uae_cdroms_dir()) { g_paths[FS_UAE_CD_PATHS].path[k++] = g_strdup( fs_uae_cdroms_dir()); } k = 0; if (relative_paths) { g_paths[FS_UAE_HD_PATHS].path[k++] = g_strdup("."); } if (g_fs_uae_config_dir_path) { g_paths[FS_UAE_HD_PATHS].path[k++] = g_strdup( g_fs_uae_config_dir_path); } if (fs_uae_hard_drives_dir()) { g_paths[FS_UAE_HD_PATHS].path[k++] = g_strdup( fs_uae_hard_drives_dir()); } k = 0; if (relative_paths) { g_paths[FS_UAE_ROM_PATHS].path[k++] = g_strdup("."); } if (g_fs_uae_config_dir_path) { g_paths[FS_UAE_ROM_PATHS].path[k++] = g_strdup( g_fs_uae_config_dir_path); } if (fs_uae_kickstarts_dir()) { g_paths[FS_UAE_ROM_PATHS].path[k++] = g_strdup( fs_uae_kickstarts_dir()); } }