summaryrefslogtreecommitdiff
path: root/src/options.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/options.c')
-rw-r--r--src/options.c647
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;
+}