diff options
Diffstat (limited to 'src/options.c')
-rw-r--r-- | src/options.c | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..ee9a577 --- /dev/null +++ b/src/options.c @@ -0,0 +1,647 @@ +/* + * This program 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 Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Eventually this should include all configuration stuff, + * for now there's few options which indicate 3do/pc flavors. + */ + +#include "options.h" + +#include "port.h" +#include "libs/graphics/gfx_common.h" +#include "libs/file.h" +#include "config.h" +#include "libs/compiler.h" +#include "libs/uio.h" +#include "libs/strlib.h" +#include "libs/log.h" +#include "libs/reslib.h" +#include "libs/memlib.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <locale.h> +#ifdef __APPLE__ +# include <libgen.h> + /* for dirname() */ +#endif + + +int optWhichCoarseScan; +int optWhichMenu; +int optWhichFonts; +int optWhichIntro; +int optWhichShield; +int optSmoothScroll; +int optMeleeScale; +const char **optAddons; + +BOOLEAN opt3doMusic; +BOOLEAN optRemixMusic; +BOOLEAN optSpeech; +BOOLEAN optSubtitles; +BOOLEAN optStereoSFX; +BOOLEAN optKeepAspectRatio; + +float optGamma; + +uio_DirHandle *contentDir; +uio_DirHandle *configDir; +uio_DirHandle *saveDir; +uio_DirHandle *meleeDir; +uio_MountHandle* contentMountHandle; + +char baseContentPath[PATH_MAX]; + +extern uio_Repository *repository; +extern uio_DirHandle *rootDir; + +INPUT_TEMPLATE input_templates[6]; + +static const char *findFileInDirs (const char *locs[], int numLocs, + const char *file); +static uio_MountHandle *mountContentDir (uio_Repository *repository, + const char *contentPath); +static void mountAddonDir (uio_Repository *repository, + uio_MountHandle *contentMountHandle, const char *addonDirName); + +static void mountDirZips (uio_DirHandle *dirHandle, const char *mountPoint, + int relativeFlags, uio_MountHandle *relativeHandle); + + +// Looks for a file 'file' in all 'numLocs' locations from 'locs'. +// returns the first element from locs where 'file' is found. +// If there is no matching location, NULL will be returned and +// errno will be set to 'ENOENT'. +// Entries from 'locs' that together with 'file' are longer than +// PATH_MAX will be ignored, except for a warning given to stderr. +static const char * +findFileInDirs (const char *locs[], int numLocs, const char *file) +{ + int locI; + char path[PATH_MAX]; + size_t fileLen; + + fileLen = strlen (file); + for (locI = 0; locI < numLocs; locI++) + { + size_t locLen; + const char *loc; + bool needSlash; + + loc = locs[locI]; + locLen = strlen (loc); + + needSlash = (locLen != 0 && loc[locLen - 1] != '/'); + if (locLen + (needSlash ? 1 : 0) + fileLen + 1 >= sizeof path) + { + // This dir plus the file name is too long. + log_add (log_Warning, "Warning: path '%s' is ignored" + " because it is too long.", loc); + continue; + } + + snprintf (path, sizeof path, "%s%s%s", loc, needSlash ? "/" : "", + file); + if (fileExists (path)) + return loc; + } + + // No matching location was found. + errno = ENOENT; + return NULL; +} + +// contentDirName is an explicitely specified location for the content, +// or NULL if none was explicitely specified. +// execFile is the path to the uqm executable, as acquired through +// main()'s argv[0]. +void +prepareContentDir (const char *contentDirName, const char* addonDirName, const char *execFile) +{ + const char *testFile = "version"; + const char *loc; + + if (contentDirName == NULL) + { + // Try the default content locations. + const char *locs[] = { + CONTENTDIR, /* defined in config.h */ + "", + "content", + "../../content" /* For running from MSVC */ + }; + loc = findFileInDirs (locs, sizeof locs / sizeof locs[0], testFile); + +#ifdef __APPLE__ + /* On OSX, if the content can't be found in any of the static + * locations, attempt to look inside the application bundle, + * by looking relative to the location of the uqm executable. */ + if (loc == NULL) + { + char *tempDir = (char *) HMalloc (PATH_MAX); + char *execFileDup; + + /* dirname can modify its argument, so we need a local + * mutable copy of it. */ + execFileDup = (char *) HMalloc (strlen (execFile) + 1); + strcpy (execFileDup, execFile); + snprintf (tempDir, PATH_MAX, "%s/../Resources/content", + dirname (execFileDup)); + loc = findFileInDirs ((const char **) &tempDir, 1, testFile); + HFree (execFileDup); + HFree (tempDir); + } +#endif + } + else + { + // Only use the explicitely supplied content dir. + loc = findFileInDirs (&contentDirName, 1, testFile); + } + if (loc == NULL) + { + log_add (log_Fatal, "Fatal error: Could not find content."); + exit (EXIT_FAILURE); + } + + if (expandPath (baseContentPath, sizeof baseContentPath, loc, + EP_ALL_SYSTEM) == -1) + { + log_add (log_Fatal, "Fatal error: Could not expand path to content " + "directory: %s", strerror (errno)); + exit (EXIT_FAILURE); + } + + log_add (log_Debug, "Using '%s' as base content dir.", baseContentPath); + contentMountHandle = mountContentDir (repository, baseContentPath); + + if (addonDirName) + log_add (log_Debug, "Using '%s' as addon dir.", addonDirName); + mountAddonDir (repository, contentMountHandle, addonDirName); + +#ifndef __APPLE__ + (void) execFile; +#endif +} + +void +prepareConfigDir (const char *configDirName) { + char buf[PATH_MAX]; + static uio_AutoMount *autoMount[] = { NULL }; + uio_MountHandle *contentHandle; + + if (configDirName == NULL) + { + configDirName = getenv("UQM_CONFIG_DIR"); + + if (configDirName == NULL) + configDirName = CONFIGDIR; + } + + if (expandPath (buf, PATH_MAX - 13, configDirName, EP_ALL_SYSTEM) + == -1) + { + // Doesn't have to be fatal, but might mess up things when saving + // config files. + log_add (log_Fatal, "Fatal error: Invalid path to config files."); + exit (EXIT_FAILURE); + } + configDirName = buf; + + log_add (log_Debug, "Using config dir '%s'", configDirName); + + // Set the environment variable UQM_CONFIG_DIR so UQM_MELEE_DIR + // and UQM_SAVE_DIR can refer to it. + setenv("UQM_CONFIG_DIR", configDirName, 1); + + // Create the path upto the config dir, if not already existing. + if (mkdirhier (configDirName) == -1) + exit (EXIT_FAILURE); + + contentHandle = uio_mountDir (repository, "/", + uio_FSTYPE_STDIO, NULL, NULL, configDirName, autoMount, + uio_MOUNT_TOP, NULL); + if (contentHandle == NULL) + { + log_add (log_Fatal, "Fatal error: Could not mount config dir: %s", + strerror (errno)); + exit (EXIT_FAILURE); + } + + configDir = uio_openDir (repository, "/", 0); + if (configDir == NULL) + { + log_add (log_Fatal, "Fatal error: Could not open config dir: %s", + strerror (errno)); + exit (EXIT_FAILURE); + } +} + +void +prepareSaveDir (void) { + char buf[PATH_MAX]; + const char *saveDirName; + + saveDirName = getenv("UQM_SAVE_DIR"); + if (saveDirName == NULL) + saveDirName = SAVEDIR; + + if (expandPath (buf, PATH_MAX - 13, saveDirName, EP_ALL_SYSTEM) == -1) + { + // Doesn't have to be fatal, but might mess up things when saving + // config files. + log_add (log_Fatal, "Fatal error: Invalid path to config files."); + exit (EXIT_FAILURE); + } + + saveDirName = buf; + setenv("UQM_SAVE_DIR", saveDirName, 1); + + // Create the path upto the save dir, if not already existing. + if (mkdirhier (saveDirName) == -1) + exit (EXIT_FAILURE); + + log_add (log_Debug, "Saved games are kept in %s.", saveDirName); + + saveDir = uio_openDirRelative (configDir, "save", 0); + // TODO: this doesn't work if the save dir is not + // "save" in the config dir. + if (saveDir == NULL) + { + log_add (log_Fatal, "Fatal error: Could not open save dir: %s", + strerror (errno)); + exit (EXIT_FAILURE); + } +} + +void +prepareMeleeDir (void) { + char buf[PATH_MAX]; + const char *meleeDirName; + + meleeDirName = getenv("UQM_MELEE_DIR"); + if (meleeDirName == NULL) + meleeDirName = MELEEDIR; + + if (expandPath (buf, PATH_MAX - 13, meleeDirName, EP_ALL_SYSTEM) == -1) + { + // Doesn't have to be fatal, but might mess up things when saving + // config files. + log_add (log_Fatal, "Fatal error: Invalid path to config files."); + exit (EXIT_FAILURE); + } + + meleeDirName = buf; + setenv("UQM_MELEE_DIR", meleeDirName, 1); + + // Create the path upto the save dir, if not already existing. + if (mkdirhier (meleeDirName) == -1) + exit (EXIT_FAILURE); + + meleeDir = uio_openDirRelative (configDir, "teams", 0); + // TODO: this doesn't work if the save dir is not + // "teams" in the config dir. + if (meleeDir == NULL) + { + log_add (log_Fatal, "Fatal error: Could not open melee teams dir: %s", + strerror (errno)); + exit (EXIT_FAILURE); + } +} + +static uio_MountHandle * +mountContentDir (uio_Repository *repository, const char *contentPath) +{ + uio_DirHandle *packagesDir; + static uio_AutoMount *autoMount[] = { NULL }; + uio_MountHandle *contentMountHandle; + + contentMountHandle = uio_mountDir (repository, "/", + uio_FSTYPE_STDIO, NULL, NULL, contentPath, autoMount, + uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); + if (contentMountHandle == NULL) + { + log_add (log_Fatal, "Fatal error: Could not mount content dir: %s", + strerror (errno)); + exit (EXIT_FAILURE); + } + + contentDir = uio_openDir (repository, "/", 0); + if (contentDir == NULL) + { + log_add (log_Fatal, "Fatal error: Could not open content dir: %s", + strerror (errno)); + exit (EXIT_FAILURE); + } + + packagesDir = uio_openDir (repository, "/packages", 0); + if (packagesDir != NULL) + { + mountDirZips (packagesDir, "/", uio_MOUNT_BELOW, contentMountHandle); + uio_closeDir (packagesDir); + } + + return contentMountHandle; +} + +static void +mountAddonDir (uio_Repository *repository, uio_MountHandle *contentMountHandle, + const char *addonDirName) +{ + uio_DirHandle *addonsDir; + static uio_AutoMount *autoMount[] = { NULL }; + uio_MountHandle *mountHandle; + uio_DirList *availableAddons; + + if (addonDirName != NULL) + { + mountHandle = uio_mountDir (repository, "addons", + uio_FSTYPE_STDIO, NULL, NULL, addonDirName, autoMount, + uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); + if (mountHandle == NULL) + { + log_add (log_Warning, "Warning: Could not mount addon directory: %s" + ";\n\t'--addon' options are ignored.", strerror (errno)); + return; + } + } + else + { + mountHandle = contentMountHandle; + } + + // NB: note the difference between addonsDir and addonDir. + // the former is the dir 'addons', the latter a directory + // in that dir. + addonsDir = uio_openDirRelative (contentDir, "addons", 0); + if (addonsDir == NULL) + { // No addon dir found. + log_add (log_Warning, "Warning: There's no 'addons' " + "directory in the 'content' directory;\n\t'--addon' " + "options are ignored."); + return; + } + + mountDirZips (addonsDir, "addons", uio_MOUNT_BELOW, mountHandle); + + availableAddons = uio_getDirList (addonsDir, "", "", match_MATCH_PREFIX); + if (availableAddons != NULL) + { + int i, count; + + // count the actual addon dirs + count = 0; + for (i = 0; i < availableAddons->numNames; ++i) + { + struct stat sb; + + if (availableAddons->names[i][0] == '.' || + uio_stat (addonsDir, availableAddons->names[i], &sb) == -1 + || !S_ISDIR (sb.st_mode)) + { // this dir entry ignored + availableAddons->names[i] = NULL; + continue; + } + ++count; + } + log_add (log_Info, "%d available addon pack%s.", count, + count == 1 ? "" : "s"); + + count = 0; + for (i = 0; i < availableAddons->numNames; ++i) + { + char mountname[128]; + uio_DirHandle *addonDir; + const char *addon = availableAddons->names[i]; + + if (!addon) + continue; + + ++count; + log_add (log_Info, " %d. %s", count, addon); + + snprintf (mountname, sizeof mountname, "addons/%s", addon); + + addonDir = uio_openDirRelative (addonsDir, addon, 0); + if (addonDir == NULL) + { + log_add (log_Warning, "Warning: directory 'addons/%s' " + "not found; addon skipped.", addon); + continue; + } + mountDirZips (addonDir, mountname, uio_MOUNT_BELOW, mountHandle); + uio_closeDir (addonDir); + } + } + else + { + log_add (log_Info, "0 available addon packs."); + } + + uio_DirList_free (availableAddons); + uio_closeDir (addonsDir); +} + +static void +mountDirZips (uio_DirHandle *dirHandle, const char *mountPoint, + int relativeFlags, uio_MountHandle *relativeHandle) +{ + static uio_AutoMount *autoMount[] = { NULL }; + uio_DirList *dirList; + + dirList = uio_getDirList (dirHandle, "", "\\.([zZ][iI][pP]|[uU][qQ][mM])$", + match_MATCH_REGEX); + if (dirList != NULL) + { + int i; + + for (i = 0; i < dirList->numNames; i++) + { + if (uio_mountDir (repository, mountPoint, uio_FSTYPE_ZIP, + dirHandle, dirList->names[i], "/", autoMount, + relativeFlags | uio_MOUNT_RDONLY, + relativeHandle) == NULL) + { + log_add (log_Warning, "Warning: Could not mount '%s': %s.", + dirList->names[i], strerror (errno)); + } + } + } + uio_DirList_free (dirList); +} + +int +loadIndices (uio_DirHandle *dir) +{ + uio_DirList *indices; + int numLoaded = 0; + + indices = uio_getDirList (dir, "", "\\.[rR][mM][pP]$", + match_MATCH_REGEX); + + if (indices != NULL) + { + int i; + + for (i = 0; i < indices->numNames; i++) + { + log_add (log_Debug, "Loading resource index '%s'", + indices->names[i]); + LoadResourceIndex (dir, indices->names[i], NULL); + numLoaded++; + } + } + uio_DirList_free (indices); + + /* Return the number of index files loaded. */ + return numLoaded; +} + +BOOLEAN +loadAddon (const char *addon) +{ + uio_DirHandle *addonsDir, *addonDir; + int numLoaded; + + addonsDir = uio_openDirRelative (contentDir, "addons", 0); + if (addonsDir == NULL) + { + // No addon dir found. + log_add (log_Warning, "Warning: There's no 'addons' " + "directory in the 'content' directory;\n\t'--addon' " + "options are ignored."); + return FALSE; + } + addonDir = uio_openDirRelative (addonsDir, addon, 0); + if (addonDir == NULL) + { + log_add (log_Warning, "Warning: Addon '%s' not found", addon); + uio_closeDir (addonsDir); + return FALSE; + } + + numLoaded = loadIndices (addonDir); + if (!numLoaded) + { + log_add (log_Error, "No RMP index files were loaded for addon '%s'", + addon); + } + + uio_closeDir (addonDir); + uio_closeDir (addonsDir); + + return (numLoaded > 0); +} + +void +prepareShadowAddons (const char **addons) +{ + uio_DirHandle *addonsDir; + const char *shadowDirName = "shadow-content"; + + addonsDir = uio_openDirRelative (contentDir, "addons", 0); + // If anything fails here, it will fail again later, so + // we'll just keep quiet about it for now + if (addonsDir == NULL) + return; + + for (; *addons != NULL; addons++) + { + const char *addon = *addons; + uio_DirHandle *addonDir; + uio_DirHandle *shadowDir; + + addonDir = uio_openDirRelative (addonsDir, addon, 0); + if (addonDir == NULL) + continue; + + // Mount addon's "shadow-content" on top of "/" + shadowDir = uio_openDirRelative (addonDir, shadowDirName, 0); + if (shadowDir) + { + log_add (log_Debug, "Mounting shadow content of '%s' addon", addon); + mountDirZips (shadowDir, "/", uio_MOUNT_ABOVE, contentMountHandle); + // Mount non-zipped shadow content + if (uio_transplantDir ("/", shadowDir, uio_MOUNT_RDONLY | + uio_MOUNT_ABOVE, contentMountHandle) == NULL) + { + log_add (log_Warning, "Warning: Could not mount shadow content" + " of '%s': %s.", addon, strerror (errno)); + } + + uio_closeDir (shadowDir); + } + uio_closeDir (addonDir); + } + + uio_closeDir (addonsDir); +} + +void +prepareAddons (const char **addons) +{ + for (; *addons != NULL; addons++) + { + log_add (log_Info, "Loading addon '%s'", *addons); + if (!loadAddon (*addons)) + { + // TODO: Should we do something like inform the user? + // Why simply refuse to load other addons? + // Maybe exit() to inform the user of the failure? + break; + } + } +} + +void +unprepareAllDirs (void) +{ + if (saveDir) + { + uio_closeDir (saveDir); + saveDir = 0; + } + if (meleeDir) + { + uio_closeDir (meleeDir); + meleeDir = 0; + } + if (contentDir) + { + uio_closeDir (contentDir); + contentDir = 0; + } + if (configDir) + { + uio_closeDir (configDir); + configDir = 0; + } +} + +bool +setGammaCorrection (float gamma) +{ + bool set = TFB_SetGamma (gamma); + if (set) + log_add (log_Info, "Gamma correction set to %.4f.", gamma); + else + log_add (log_Warning, "Unable to set gamma correction."); + return set; +} |