/** * \brief Creates a new file in the override directory. * \param self Paths. * \param name Filename. * \param config One for a config file, zero for a data file. * \return path Absolute path or NULL. */ const char* lipth_paths_create_file ( LIPthPaths* self, const char* name, int config) { char* path; LIAlgStrdicNode* node; /* Format the path. */ if (config) path = lisys_path_concat (self->module_config, name, NULL); else path = lisys_path_concat (self->module_data_save, name, NULL); if (path == NULL) return NULL; /* Register the file. */ node = lialg_strdic_find_node (self->files, name); if (node != NULL) { lisys_free (node->value); node->value = path; } else lialg_strdic_insert (self->files, name, path); return path; }
/** * \brief Finds the data of a config file by a path relative to the data directory root. * \param self Paths. * \param path Path relative to the data directory root. * \param config Nonzero for a config file, zero for a data file. * \return Absolute path or NULL. */ char* lipth_paths_find_path ( const LIPthPaths* self, const char* path, int config) { char* path1; /* Try the config/save path. */ if (config) path1 = lisys_path_concat (self->module_config, path, NULL); else path1 = lisys_path_concat (self->module_data_save, path, NULL); if (path1 == NULL) return NULL; if (lisys_filesystem_access (path1, LISYS_ACCESS_READ)) return path1; lisys_free (path1); /* Try the data path. */ path1 = lisys_path_concat (self->module_data, path, NULL); if (lisys_filesystem_access (path1, LISYS_ACCESS_READ)) return path1; lisys_free (path1); return NULL; }
/** * \brief Gets the home data directory. * * Follows the XDG Base Directory Specification: * http://www.freedesktop.org/Standards/basedir-spec * * \return New string or NULL. */ char* lisys_paths_get_data_home () { #ifdef __WIN32__ int i; char tmp[MAX_PATH]; if (!SHGetSpecialFolderPath (NULL, tmp, CSIDL_PERSONAL, TRUE)) return NULL; for (i = 0 ; tmp[i] != '\0' ; i++) { if (tmp[i] == '\\') tmp[i] = '/'; } return lisys_path_concat (tmp, "My Games", NULL); #else char* ret; char* tmp; const char* dir; dir = getenv ("XDG_DATA_HOME"); if (dir != NULL && dir[0] != '\0') return lisys_string_dup (dir); tmp = lisys_paths_get_home (); if (tmp == NULL) return NULL; ret = lisys_path_concat (tmp, ".local/share", NULL); lisys_free (tmp); return ret; #endif }
/** * \brief Adds a data directory lookup path. * \param self Paths. * \param path Module root relative path. */ int lipth_paths_add_path ( LIPthPaths* self, const char* path) { int ret = 1; char* path1; /* Add the module directory. */ path1 = lisys_path_concat (self->module_data, path, NULL); if (path1 != NULL) { if (!lipth_paths_add_path_abs (self, path1)) ret = 0; lisys_free (path1); } else ret = 0; /* Add the override directory. */ path1 = lisys_path_concat (self->module_data_save, path, NULL); if (path1 != NULL) { lipth_paths_add_path_abs (self, path1); lisys_free (path1); } else ret = 0; return ret; }
/** * \brief Gets the path to an SQL database. * * Calling this function will create the save directory if it doesn't exist * yet. If the creation fails or the function runs out of memory, NULL is * returned and the error message is set. * * \param self Paths object. * \param name File name. * \return Newly allocated absolute path or NULL. */ char* lipth_paths_get_sql ( const LIPthPaths* self, const char* name) { char* path; /* Format the path. */ path = lisys_path_concat (self->module_state, name, NULL); if (path == NULL) return NULL; /* Check if the save directory exists. */ if (lisys_filesystem_access (self->module_state, LISYS_ACCESS_EXISTS)) { if (!lisys_filesystem_access (self->module_state, LISYS_ACCESS_WRITE)) { lisys_error_set (EINVAL, "save path `%s' is not writable", path); lisys_free (path); return NULL; } return path; } /* Create the save directory. */ if (!lisys_filesystem_makepath (self->module_state)) { lisys_free (path); return NULL; } return path; }
/** * \brief Gets the path to a sound file. * * \param self Paths object. * \param name File name. * \return Full path or NULL. */ char* lipth_paths_get_sound ( const LIPthPaths* self, const char* name) { char* path; /* Try the override path. */ path = lisys_path_concat (self->override_data, "sounds", name, NULL); if (path == NULL) return NULL; if (lisys_filesystem_access (path, LISYS_ACCESS_READ)) return path; lisys_free (path); /* Try the real path. */ return lisys_path_concat (self->module_data, "sounds", name, NULL); }
/** * \brief Gets the global data directory. * * Follows the XDG Base Directory Specification: * http://www.freedesktop.org/Standards/basedir-spec * * \param path Relative path being searched for. * \return New string or NULL. */ char* lisys_paths_get_data_global ( const char* path) { #ifdef __WIN32__ return NULL; #else int last; char* dup; char* ptr; char* ret; char* start; const char* dirs; /* Get the list of global data directories. */ dirs = getenv ("XDG_DATA_DIRS"); if (dirs == NULL || dirs[0] == '\0') return NULL; dup = lisys_string_dup (dirs); if (dup == NULL) return NULL; /* Loop through all directories. */ ptr = start = dup; ret = NULL; last = 0; while (1) { /* Search for the delimiter. */ last = (*ptr == '\0'); if (*ptr != ':' && !last) { ptr++; continue; } *ptr = '\0'; /* Test if the path is valid. */ ret = lisys_path_concat (start, path, NULL); if (ret != NULL) { if (lisys_filesystem_access (ret, LISYS_ACCESS_READ)) break; lisys_free (ret); ret = NULL; } /* Check if more candidates exist. */ if (last) break; ptr++; start = ptr; } /* Return the result or NULL. */ lisys_free (dup); return ret; #endif }
/** * \brief Gets the home cache directory. * * Follows the XDG Base Directory Specification: * http://www.freedesktop.org/Standards/basedir-spec * * \return New string or NULL. */ char* lisys_paths_get_cache_home () { #ifdef __WIN32__ return lisys_paths_get_data_home (); #else char* ret; char* tmp; const char* dir; dir = getenv ("XDG_CACHE_HOME"); if (dir != NULL && dir[0] != '\0') return lisys_string_dup (dir); tmp = lisys_paths_get_home (); if (tmp == NULL) return NULL; ret = lisys_path_concat (tmp, ".cache", NULL); lisys_free (tmp); return ret; #endif }
/** * \brief Creates a new paths object. * * \param path Package root directory or NULL for auto-detect. * \param name Module name. * \return Paths or NULL. */ LIPthPaths* lipth_paths_new ( const char* path, const char* name) { char* tmp; LIPthPaths* self; /* Allocate self. */ self = lisys_calloc (1, sizeof (LIPthPaths)); if (self == NULL) return NULL; /* Allocate the file lookup table. */ self->files = lialg_strdic_new (); if (self->files == NULL) { lipth_paths_free (self); return NULL; } /* Set the module name. */ self->module_name = lisys_string_dup (name); if (self->module_name == NULL) { lipth_paths_free (self); return NULL; } /* Set the root directory. */ if (path != NULL) { self->root = lisys_string_dup (path); if (self->root == NULL) { lipth_paths_free (self); return NULL; } } else { self->root = lipth_paths_get_root (); if (self->root == NULL) { lipth_paths_free (self); return NULL; } } /* Get the data directory root. */ /* This is where modules and system scripts are located. If relative paths are enabled, the directory of the executable is searched for a data directory. Failing that, XDG data directories are searched for the game directory. As the last resort, the hardcoded data directory is used. */ #ifdef LI_RELATIVE_PATHS self->global_data = lisys_path_concat (self->root, "data", NULL); if (!private_validate_dir (&self->global_data)) #endif { self->global_data = lisys_paths_get_data_global ("lipsofsuna"); private_validate_dir (&self->global_data); if (self->global_data == NULL) { #ifdef LIDATADIR self->global_data = lisys_string_dup (LIDATADIR); if (!private_validate_dir (&self->global_data)) #endif { lisys_error_set (EINVAL, "cannot find the data directory"); lipth_paths_free (self); return NULL; } } } /* Get the data directory of the current module. */ /* This is where the data files of the module are installed. The directory functions as a fallback for both data and configuration files. */ self->module_data = lisys_path_concat (self->global_data, name, NULL); if (!private_validate_dir (&self->module_data)) { lisys_error_set (EINVAL, "cannot find the module data directory"); lipth_paths_free (self); return NULL; } /* Get the data save directory. */ /* This is where save files are written and where the user can copy any custom data files or mods. It's the first place where data files are looked for. */ tmp = lisys_paths_get_data_home (); if (tmp == NULL) { lipth_paths_free (self); return NULL; } self->module_data_save = lisys_path_concat (tmp, "lipsofsuna", name, NULL); lisys_free (tmp); if (self->module_data_save == NULL) { lipth_paths_free (self); return NULL; } /* Get the config directory. */ /* This is where configuration files are stored. It's separate from the save directory since in Linux that's the case. In Windows it's actually the save directory. */ tmp = lisys_paths_get_config_home (); if (tmp == NULL) { lipth_paths_free (self); return NULL; } self->module_config = lisys_path_concat (tmp, "lipsofsuna", name, NULL); lisys_free (tmp); if (self->module_config == NULL) { lipth_paths_free (self); return NULL; } /* Get the extension directory. */ /* This directory is reserved for potential third party extension libraries. The path is lib/extensions either in the relative path, XDG data path or the hardcoded extension directory. */ #ifdef LI_RELATIVE_PATHS self->global_exts = lisys_path_concat (self->global_data, "lib", "extensions", NULL); if (!private_validate_dir (&self->global_exts)) #endif { self->global_exts = lisys_paths_get_data_global ("lipsofsuna/lib/extensions"); #ifdef LIEXTSDIR if (!private_validate_dir (&self->global_exts)) { self->global_exts = lisys_string_dup (LIEXTSDIR); private_validate_dir (&self->global_exts); } #endif } /* Create the save directories. */ if (!private_create_save_path (self, self->module_data_save) || !private_create_save_path (self, self->module_config)) { lipth_paths_free (self); return NULL; } /* Add the base data directory. */ lipth_paths_add_path (self, ""); return self; }
/** * \brief Creates a new paths object. * * \param path Package root directory or NULL for auto-detect. * \param name Module name. * \return Paths or NULL. */ LIPthPaths* lipth_paths_new ( const char* path, const char* name) { char* tmp; LIPthPaths* self; LISysStat stat; self = lisys_calloc (1, sizeof (LIPthPaths)); if (self == NULL) return NULL; /* Set module name. */ self->module_name = lisys_string_dup (name); if (self->module_name == NULL) goto error; /* Set root directory. */ if (path != NULL) { self->root = lisys_string_dup (path); if (self->root == NULL) goto error; } else { self->root = lipth_paths_get_root (); if (self->root == NULL) goto error; } /* Get data directory. */ #ifdef LI_RELATIVE_PATHS self->global_data = lisys_path_concat (self->root, "data", NULL); if (self->global_data == NULL) goto error; #else self->global_data = LIDATADIR; #endif self->module_data = lisys_path_concat (self->global_data, name, NULL); if (self->module_data == NULL) goto error; /* Get data override directory. */ tmp = lisys_paths_get_data_home (); if (tmp == NULL) goto error; self->override_data = lisys_path_concat (tmp, "lipsofsuna", "data", name, NULL); if (self->override_data == NULL) { lisys_free (tmp); goto error; } /* Get save directory. */ self->global_state = lisys_path_concat (tmp, "lipsofsuna", "save", NULL); lisys_free (tmp); if (self->global_state == NULL) goto error; self->module_state = lisys_path_concat (self->global_state, name, NULL); if (self->module_state == NULL) goto error; /* Get extension directory. */ #ifdef LI_RELATIVE_PATHS self->global_exts = lisys_path_concat (self->global_data, "lib", "extensions", NULL); if (self->global_exts == NULL) goto error; #else self->global_exts = LIEXTSDIR; #endif /* Check for valid data directory. */ if (!lisys_filesystem_stat (self->module_data, &stat)) { lisys_error_set (EIO, "missing data directory `%s'", self->module_data); goto error; } if (stat.type != LISYS_STAT_DIRECTORY && stat.type != LISYS_STAT_LINK) { lisys_error_set (EIO, "invalid data directory `%s': not a directory", self->module_data); goto error; } return self; error: lipth_paths_free (self); return NULL; }