/* * 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 #include #include #include #include #ifdef __APPLE__ # include /* 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; }